package com.stottlerhenke.simbionic.editor.gui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.datatransfer.StringSelection;
import java.awt.dnd.Autoscroll;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragSource;
import java.awt.dnd.DragSourceDragEvent;
import java.awt.dnd.DragSourceDropEvent;
import java.awt.dnd.DragSourceEvent;
import java.awt.dnd.DragSourceListener;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.awt.event.ActionEvent;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.StreamTokenizer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
import java.util.Vector;
import java.util.regex.Pattern;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JEditorPane;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;

import com.stottlerhenke.simbionic.api.SB_VarType;
import com.stottlerhenke.simbionic.common.SB_Logger;
import com.stottlerhenke.simbionic.common.classes.SB_ClassMap;
import com.stottlerhenke.simbionic.editor.ClassSpecLoader;
import com.stottlerhenke.simbionic.editor.ETypeType;
import com.stottlerhenke.simbionic.editor.ETypeValid;
import com.stottlerhenke.simbionic.editor.SB_Action;
import com.stottlerhenke.simbionic.editor.SB_Behavior;
import com.stottlerhenke.simbionic.editor.SB_CancelException;
import com.stottlerhenke.simbionic.editor.SB_Class;
import com.stottlerhenke.simbionic.editor.SB_ClassMethod;
import com.stottlerhenke.simbionic.editor.SB_ClassSpecificationFileImport;
import com.stottlerhenke.simbionic.editor.SB_Constant;
import com.stottlerhenke.simbionic.editor.SB_EditorDataInterface;
import com.stottlerhenke.simbionic.editor.SB_ErrorInfo;
import com.stottlerhenke.simbionic.editor.SB_Folder;
import com.stottlerhenke.simbionic.editor.SB_Function;
import com.stottlerhenke.simbionic.editor.SB_Global;
import com.stottlerhenke.simbionic.editor.SB_Package;
import com.stottlerhenke.simbionic.editor.SB_Parameter;
import com.stottlerhenke.simbionic.editor.SB_Predicate;
import com.stottlerhenke.simbionic.editor.SB_TypeChangeListener;
import com.stottlerhenke.simbionic.editor.SB_TypeManager;
import com.stottlerhenke.simbionic.editor.SB_Variable;
import com.stottlerhenke.simbionic.editor.SimBionicEditor;
import com.stottlerhenke.simbionic.editor.UserObject;
import com.stottlerhenke.simbionic.editor.Util;
import com.stottlerhenke.simbionic.editor.gui.api.EditorRegistry;
import com.stottlerhenke.simbionic.editor.gui.api.I_CompileValidator;
import com.stottlerhenke.simbionic.editor.gui.api.I_EditorListener;
import com.stottlerhenke.simbionic.editor.gui.api.I_ExpressionEditor;
import com.stottlerhenke.simbionic.editor.parser.SB_ParserEditorInterface;

public class SB_Catalog extends EditorTree implements Autoscroll, DragSourceListener,
        DragGestureListener, DropTargetListener, SB_TypeChangeListener, ClassSpecLoader
{
    final static int kPolyIndexInsert = 0;
    final static int kPolyIndexDelete = 1;
    final static int kPolyIndexRename = 2;
    final static int kPolyIndexMoveUp = 3;
    final static int kPolyIndexMoveDown = 4;
    final static int kPolyIndexSelect = 5;

    protected static ImageIcon _projectIcon = null;
    protected static ImageIcon _headingIcon = null;

    protected static final int START_USER_ID = 128;
    public JComboBox _comboBehav;

    public DefaultMutableTreeNode _actions, _predicates, _behaviors, _constants, _globals, _types;
    public SB_Behavior _main;
    
    // root package stores all class specification info
    protected SB_Package _rootPackage;
    
    protected SB_ClassMap _classMap;

    // functions popup (actions/predicates/behaviors/folder)
    protected JPopupMenu _functionsPopup;
    protected JMenuItem _insertActionItem; // actions only
    protected JMenuItem _insertPredicateItem; // predicates only
    protected JMenuItem _insertBehaviorItem; // behaviors only
    protected JMenuItem _importBehaviorItem; // behaviors only
    protected JMenuItem _duplicateBehaviorItem; // behaviors only
    protected JMenuItem _newFolderItem;
    protected JPopupMenu.Separator _separator;
    protected JMenuItem _renameFolderItem;
    protected JMenuItem _deleteFolderItem;

    // constants popup
    protected JPopupMenu _constantsPopup;
    protected JMenuItem _insertConstantItem;

    // globals popup
    protected JPopupMenu _globalsPopup;
    protected JMenuItem _insertGlobalItem;

    // function popup (action/predicate/behavior)
    protected JPopupMenu _functionPopup;
    protected JMenuItem _insertParameterItem;
    protected JMenuItem _renameFunctionItem;
    protected JMenuItem _deleteFunctionItem;
    protected JMenu _retTypeSubmenu; // predicate only
    protected ButtonGroup _retTypeSubmenuButtonGroup; // predicate only
    protected JRadioButtonMenuItem[] _retTypeItems;
    protected JMenu _execSubmenu; // behavior only
    protected JRadioButtonMenuItem[] _execItems;
    protected JMenu _interruptSubmenu; // behavior only
    protected JRadioButtonMenuItem[] _interruptItems;
    protected JPopupMenu.Separator _descriptionSeparator;
    protected JMenuItem _descriptionItem;
    protected JPopupMenu.Separator _reservedSeparator;
    protected JCheckBoxMenuItem _reservedItem;

    // variable popup (parameter/constant/global)
    protected JPopupMenu _variablePopup;
    protected JMenuItem _renameVariableItem;
    protected JMenuItem _deleteVariableItem;
    protected JMenu _typeSubmenu;
    protected ButtonGroup _typeSubmenuButtonGroup;
    protected JMenuItem[] _typeItems;
    protected JMenu _directionSubmenu; // parameter only
    protected JRadioButtonMenuItem[] _directionItems;
    protected JMenuItem _valueItem; // constant only
    protected JMenuItem _initialValueItem; // global only
    protected JMenuItem _moveUpItem;
    protected JMenuItem _moveDownItem;
    
    // types popup (classes)
    protected JPopupMenu _typesPopup;
    protected JMenuItem _insertClassItem;	// TODO
    protected JMenuItem _importClassSpecItem;
    protected JMenuItem _classSpecGeneratorItem;
   
    
    // package popup
    protected JPopupMenu _packagePopup;
    protected JMenuItem _insertPackageClassItem;	// TODO
    protected JMenuItem _removePackageItem;
    
    // class popup
    protected JPopupMenu _classPopup;
    protected JMenuItem _editClassItem;		// TODO
    protected JMenuItem _editClassIdItem;
    protected JMenuItem _removeClassItem;
    
    private SB_TypeManager _typeManager;
    
    // description dialog
    protected static JDialog _descriptionDialog = null;
    protected static JEditorPane _descriptionTextArea;
    protected static JButton _descriptionOK;
    protected static JButton _descriptionCancel;

    // constant value dialog
    protected static JDialog _constantValueDialog = null;
    protected static JTextField _constantValueTextField;
    protected static JButton _constantValueOK;
    protected static JButton _constantValueCancel;

    // initial value dialog
    protected static JDialog _initialValueDialog = null;
    protected static JTextField _initialValueTextField;
    protected static JButton _initialValueOK;
    protected static JButton _initialValueCancel;

    protected static SetInitialValueDialog _setInitialArrayValueDialog = null;
    protected static SetInitialValueDialog _setInitialTableValueDialog = null;
    
    // edit class type id dialog
    protected static JDialog _editClassIdDialog = null;
    protected static JTextField _classIdField;
    protected static JButton _editClassIdOK;
    protected static JButton _editClassIdCancel;
    
    protected static ClassSpecGeneratorDialog _classSpecGeneratorDialog;
    protected static ImportClassSpecDialog _importClassSpecDialog;
    
    protected DragSource _dragSource = null;
    private static BufferedImage _image = new BufferedImage(100, 75, BufferedImage.TYPE_3BYTE_BGR);
    protected boolean _holdDrag = false;
    protected DefaultMutableTreeNode _dragNode = null;
    protected int _dragRow = -1;
    protected DropTarget _dropTarget = null;

    public SB_Catalog(SimBionicEditor editor)
    {
        super(editor);

        createBehaviorCombo();

        _actions = new DefaultMutableTreeNode("Actions");
        _root.add(_actions);
        _predicates = new DefaultMutableTreeNode("Predicates");
        _root.add(_predicates);
        _behaviors = new DefaultMutableTreeNode("Behaviors");
        _root.add(_behaviors);
        _globals = new DefaultMutableTreeNode("Globals");
        _root.add(_globals);
        _constants = new DefaultMutableTreeNode("Constants");
        _root.add(_constants);
        _types = new DefaultMutableTreeNode("Types");
        _root.add(_types);
        _rootPackage = new SB_Package();

        _functionsPopup = new JPopupMenu();
        _insertActionItem = new JMenuItem("Insert Action");
        _insertActionItem.addActionListener(this);
        _functionsPopup.add(_insertActionItem);
        _insertPredicateItem = new JMenuItem("Insert Predicate");
        _insertPredicateItem.addActionListener(this);
        _functionsPopup.add(_insertPredicateItem);
        _insertBehaviorItem = new JMenuItem("Insert Behavior");
        _insertBehaviorItem.addActionListener(this);
        _functionsPopup.add(_insertBehaviorItem);
        _importBehaviorItem = new JMenuItem("Import Behavior...");
        _importBehaviorItem.addActionListener(this);
        _functionsPopup.add(_importBehaviorItem);
        _newFolderItem = new JMenuItem("New Folder");
        _newFolderItem.addActionListener(this);
        _functionsPopup.add(_newFolderItem);
        _separator = new JPopupMenu.Separator();
        _functionsPopup.add(_separator);
        _renameFolderItem = new JMenuItem("Rename");
        _renameFolderItem.addActionListener(this);
        _functionsPopup.add(_renameFolderItem);
        _deleteFolderItem = new JMenuItem("Delete");
        _deleteFolderItem.addActionListener(this);
        _functionsPopup.add(_deleteFolderItem);

        _constantsPopup = new JPopupMenu();
        _insertConstantItem = new JMenuItem("Insert Constant");
        _insertConstantItem.addActionListener(this);
        _constantsPopup.add(_insertConstantItem);

        _globalsPopup = new JPopupMenu();
        _insertGlobalItem = new JMenuItem("Insert Global");
        _insertGlobalItem.addActionListener(this);
        _globalsPopup.add(_insertGlobalItem);

        _functionPopup = new JPopupMenu();
        _duplicateBehaviorItem = new JMenuItem("Duplicate Behavior");
        _duplicateBehaviorItem.addActionListener(this);
        _functionPopup.add(_duplicateBehaviorItem);
        _insertParameterItem = new JMenuItem("Insert Parameter");
        _insertParameterItem.addActionListener(this);
        _functionPopup.add(_insertParameterItem);
        _functionPopup.addSeparator();
        _renameFunctionItem = new JMenuItem("Rename");
        _renameFunctionItem.addActionListener(this);
        _functionPopup.add(_renameFunctionItem);
        _deleteFunctionItem = new JMenuItem("Delete");
        _deleteFunctionItem.addActionListener(this);
        _functionPopup.add(_deleteFunctionItem);
        _functionPopup.addSeparator();
        
        initRetTypeSubMenu();

        _execSubmenu = new JMenu("Set Execution");
        ButtonGroup group = new ButtonGroup();
        int length = SB_Behavior.kExecNames.length;
        _execItems = new JRadioButtonMenuItem[length];
        for (int i = 0; i < length; ++i)
        {
            _execItems[i] = new JRadioButtonMenuItem(SB_Behavior.kExecNames[i]);
            _execItems[i].addActionListener(this);
            group.add(_execItems[i]);
            _execSubmenu.add(_execItems[i]);
        }
        _functionPopup.add(_execSubmenu);

        _interruptSubmenu = new JMenu("Set Interruptibility");
        group = new ButtonGroup();
        length = SB_Behavior.kInterruptNames.length;
        _interruptItems = new JRadioButtonMenuItem[length];
        for (int i = 0; i < length; ++i)
        {
            _interruptItems[i] = new JRadioButtonMenuItem(SB_Behavior.kInterruptNames[i]);
            _interruptItems[i].addActionListener(this);
            group.add(_interruptItems[i]);
            _interruptSubmenu.add(_interruptItems[i]);
        }
        _functionPopup.add(_interruptSubmenu);

        _descriptionSeparator = new JPopupMenu.Separator();
        _functionPopup.add(_descriptionSeparator);
        _descriptionItem = new JMenuItem("Set Description...");
        _descriptionItem.addActionListener(this);
        _functionPopup.add(_descriptionItem);

        if (SimBionicEditor.DEV)
        {
            _reservedSeparator = new JPopupMenu.Separator();
            _functionPopup.add(_reservedSeparator);
            _reservedItem = new JCheckBoxMenuItem("Reserved");
            _reservedItem.addActionListener(this);
            _functionPopup.add(_reservedItem);
        }

        _variablePopup = new JPopupMenu();
        _renameVariableItem = new JMenuItem("Rename");
        _renameVariableItem.addActionListener(this);
        _variablePopup.add(_renameVariableItem);
        _deleteVariableItem = new JMenuItem("Delete");
        _deleteVariableItem.addActionListener(this);
        _variablePopup.add(_deleteVariableItem);
        _variablePopup.addSeparator();

        initTypeSubMenu();

        _directionSubmenu = new JMenu("Set Direction");
        group = new ButtonGroup();
        length = SB_Parameter.kDirectionNames.length;
        _directionItems = new JRadioButtonMenuItem[length];
        for (int i = 0; i < length; ++i)
        {
            _directionItems[i] = new JRadioButtonMenuItem(SB_Parameter.kDirectionNames[i]);
            _directionItems[i].addActionListener(this);
            group.add(_directionItems[i]);
            _directionSubmenu.add(_directionItems[i]);
        }
        _variablePopup.add(_directionSubmenu);

        _valueItem = new JMenuItem("Set Value...");
        _valueItem.addActionListener(this);
        _variablePopup.add(_valueItem);
        _initialValueItem = new JMenuItem("Set Initial Value...");
        _initialValueItem.addActionListener(this);
        _variablePopup.add(_initialValueItem);

        _variablePopup.addSeparator();
        _moveUpItem = new JMenuItem("Move Up");
        _moveUpItem.addActionListener(this);
        _variablePopup.add(_moveUpItem);
        _moveDownItem = new JMenuItem("Move Down");
        _moveDownItem.addActionListener(this);
        _variablePopup.add(_moveDownItem);
        
        _typesPopup = new JPopupMenu();
        _classSpecGeneratorItem = new JMenuItem("Create Class Specification");
        _classSpecGeneratorItem.addActionListener(this);
        _typesPopup.add(_classSpecGeneratorItem);

        _importClassSpecItem = new JMenuItem("Import Class Specification");
        _importClassSpecItem.addActionListener(this);
        _typesPopup.add(_importClassSpecItem);
        
        _packagePopup = new JPopupMenu();
        _removePackageItem = new JMenuItem("Delete Package");
        _removePackageItem.addActionListener(this);
        _packagePopup.add(_removePackageItem);
        
        _classPopup = new JPopupMenu();
        _removeClassItem = new JMenuItem("Delete Class");
        _removeClassItem.addActionListener(this);
        _classPopup.add(_removeClassItem);
        _editClassIdItem = new JMenuItem("Edit Class Id");
        _editClassIdItem.addActionListener(this);
        _classPopup.add(_editClassIdItem);
        

        addFocusListener(new FocusListener()
        {
            public void focusGained(FocusEvent event)
            {

            }

            public void focusLost(FocusEvent event)
            {
            }
        });

        SB_EditorDataInterface.setCatalog(this);

        _dragSource = new DragSource();
        _dragSource.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_MOVE, this);
        _dropTarget = new DropTarget(this, this);
    }
    
    private void initTypeSubMenu(){
        _typeSubmenu = new JMenu("Set Type");
        _variablePopup.add(_typeSubmenu);
    }
    
    private void initRetTypeSubMenu(){
        _retTypeSubmenu = new JMenu("Set Return Type");
        _functionPopup.add(_retTypeSubmenu);
    }
    
    
    /*
     * Populates the type submenu with the types get from type manager.
     */
    private void populateTypeSubMenu(boolean isConstant, boolean isParam)
    {
    	_typeSubmenuButtonGroup = new ButtonGroup();
    	ArrayList items = _typeManager.getVarComboItems(isConstant, isParam);
    	_typeItems = new JMenuItem[items.size()];
    	for (int i = 0; i < _typeItems.length; i ++)
    	{
    		// creates all menu items;
            _typeItems[i] = new JRadioButtonMenuItem((String)items.get(i));
            _typeItems[i].addActionListener(this);
            
            // but just adds the non-class ones first;
            if (_typeManager.varComboIndexToId(i, isConstant, isParam).getState() < SB_VarType.kCLASS_START.getState())
            {
            _typeSubmenu.add(_typeItems[i]);
            }
            _typeSubmenuButtonGroup.add(_typeItems[i]);
    	}
    	
    	// now, creates package sub-menus
    	HashMap packageMenus = new HashMap();
    	String[] packageItems = _typeManager.getPackageItems();
    	for (int i = 0; i < packageItems.length; i ++)
    	{
    		JMenu packageMenu = new JMenu(packageItems[i]);
    		packageMenus.put(packageItems[i], packageMenu);
    	}
    	
    	// now, adds class menus in proper package sub-menus
    	for (int i = 0; i < items.size(); i ++)
    	{
    		SB_VarType id = _typeManager.varComboIndexToId(i, isConstant, isParam);
    		if (id.getState() >= SB_VarType.kCLASS_START.getState())
    		{
    			String typePackage = _typeManager.getTypePackage(id);
    			int idx = typePackage.lastIndexOf('.');
    			String packageName = (idx < 0 ? "(default package)" : typePackage.substring(0, idx));
    			JMenu packageMenu = (JMenu) packageMenus.get(packageName);
    			int j = 0;
    			for (; j < packageMenu.getItemCount(); j++)
    			{
    				if (_typeItems[i].getText().compareTo(packageMenu.getItem(j).getText()) < 0)
    					break;
    			}
    			packageMenu.insert(_typeItems[i], j);
    		}
    	}
    	
    	// now, adds package sub-menus if it is not empty
    	for (int i = 0; i < packageItems.length; i ++)
    	{
    		JMenu packageMenu = (JMenu) packageMenus.get(packageItems[i]);
    		if (packageMenu.getItemCount() > 0)
    			_typeSubmenu.add(packageMenu);
    	}
    }

    /*
     * Call this method to clear type sub menu items before 
     * re-populating menu items to prevent memory leaks.
     */
    private void clearTypeSubMenu(){
    	if (_typeItems != null)
    	{
    		for (int i = 0; i < _typeItems.length; i ++)
    		{
    			_typeItems[i].removeActionListener(this);
    			_typeSubmenuButtonGroup.remove(_typeItems[i]);
    			_typeItems[i] = null;
    		}
    		
    		_typeSubmenu.removeAll();
    		_typeSubmenuButtonGroup = null;
    		_typeItems = null;
    	}
    }
    
    /*
     * Populates the return type submenu with the types get from type manager.
     */
    private void populateRetTypeSubMenu(){
    	_retTypeSubmenuButtonGroup = new ButtonGroup();
    	ArrayList items = _typeManager.getReturnValueComboItems();
    	_retTypeItems = new JRadioButtonMenuItem[items.size()];
    	for (int i = 0; i < _retTypeItems.length; i ++)
    	{
    		// creates all menu items;
    		_retTypeItems[i] = new JRadioButtonMenuItem((String)items.get(i));
    		_retTypeItems[i].addActionListener(this);
    		
    		// but only adds the non-class ones first;
    		if (_typeManager.returnValueComboIndexToId(i).getState() < SB_VarType.kCLASS_START.getState())
    		{
    		_retTypeSubmenu.add(_retTypeItems[i]);
    		}
            _retTypeSubmenuButtonGroup.add(_retTypeItems[i]);
    	}
    	
    	// now, creates package sub-menus
    	HashMap packageMenus = new HashMap();
    	String[] packageItems = _typeManager.getPackageItems();
    	for (int i = 0; i < packageItems.length; i ++)
    	{
    		JMenu packageMenu = new JMenu(packageItems[i]);
    		packageMenus.put(packageItems[i], packageMenu);
    	}
    	
    	// now, adds class menus in proper package sub-menus
    	for (int i = 0; i < items.size(); i ++)
    	{
    		SB_VarType id = _typeManager.returnValueComboIndexToId(i);
    		if (id.getState() >= SB_VarType.kCLASS_START.getState())
    		{
    			String typePackage = _typeManager.getTypePackage(id);
    			int idx = typePackage.lastIndexOf('.');
    			String packageName = (idx < 0 ? "(default package)" : typePackage.substring(0, idx));
    			JMenu packageMenu = (JMenu) packageMenus.get(packageName);
    			int j = 0;
    			for (; j < packageMenu.getItemCount(); j++)
    			{
    				if (_retTypeItems[i].getText().compareTo(packageMenu.getItem(j).getText()) < 0)
    					break;
    			}
    			packageMenu.insert(_retTypeItems[i], j);
    		}
    	}
    	
    	// now, adds package sub-menus if it is not empty
    	for (int i = 0; i < packageItems.length; i ++)
    	{
    		JMenu packageMenu = (JMenu) packageMenus.get(packageItems[i]);
    		if (packageMenu.getItemCount() > 0)
    			_retTypeSubmenu.add(packageMenu);
    	}
    }
    
    /*
     * Call this method to clear return type sub menu items before 
     * re-populating menu items to prevent memory leaks.
     */
    private void clearRetTypeSubMenu(){
    	if (_retTypeItems != null)
    	{
    		for (int i = 0; i < _retTypeItems.length; i ++)
    		{
    			_retTypeItems[i].removeActionListener(this);
    			_retTypeSubmenuButtonGroup.remove(_retTypeItems[i]);
    			_retTypeItems[i] = null;
    		}
    		
    		_retTypeSubmenu.removeAll();
    		_retTypeSubmenuButtonGroup = null;
    		_retTypeItems = null;
    	}
    }
    
    protected SimBionicEditor getEditor()
    {
        return  _editor;
    }

    protected void newCatalog()
    {
        _root.setUserObject("Catalog");

        try
        {
            loadBase();
        } catch (Exception exception)
        {
            System.err.println("error parsing base.dat: " + exception.getMessage());
            exception.printStackTrace();
        }
        newBehaviors();
        newGlobals();
        newConstants();
        newTypes();
        _rootPackage = new SB_Package();

        DefaultTreeModel treeModel = (DefaultTreeModel) this.treeModel;
        treeModel.reload();

        SB_TabbedCanvas tabbedCanvas = ComponentRegistry.getContent();
        if (tabbedCanvas != null)
        {
            tabbedCanvas._behavior = null;
            tabbedCanvas.setBehavior(_main, false);
        }
    }

    public void clearCatalog()
    {
        _root.setUserObject("Catalog");
        _actions.removeAllChildren();
        _predicates.removeAllChildren();
        _behaviors.removeAllChildren();
        _globals.removeAllChildren();
        _constants.removeAllChildren();
        _types.removeAllChildren();
    }

    protected void newActions()
    {
        _actions.removeAllChildren();
    }

    protected void newPredicates()
    {
        _predicates.removeAllChildren();
    }

    protected void loadBase() throws Exception
    {
        loadBase(false);
    }

    protected void loadBase(boolean update) throws Exception
    {
        DefaultMutableTreeNode coreActions = null;
        DefaultMutableTreeNode corePredicates = null;
        if (update)
        {
            DefaultTreeModel treeModel = (DefaultTreeModel) this.treeModel;
            coreActions = findNodeByName("Core Actions", _actions);
            int size = coreActions.getChildCount();
            for (int i = size - 1; i >= 0; --i)
                treeModel.removeNodeFromParent((DefaultMutableTreeNode) coreActions.getChildAt(i));

            corePredicates = findNodeByName("Core Predicates", _predicates);
            size = corePredicates.getChildCount();
            for (int i = size - 1; i >= 0; --i)
                treeModel.removeNodeFromParent((DefaultMutableTreeNode) corePredicates
                        .getChildAt(i));

        } else
        {
            _actions.removeAllChildren();
            SB_Folder folder = new SB_Folder("Core Actions");
            folder.setEditable(false);
            coreActions = new DefaultMutableTreeNode(folder);
            _actions.add(coreActions);
            _predicates.removeAllChildren();
            folder = new SB_Folder("Core Predicates");
            folder.setEditable(false);
            corePredicates = new DefaultMutableTreeNode(folder);
            _predicates.add(corePredicates);
        }

        try
        {
            BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(
                    "data/base.dat")));
            StreamTokenizer stream = new StreamTokenizer(in);

            char[] cbuf = new char[512];
            stream.nextToken();
            if (stream.ttype != StreamTokenizer.TT_NUMBER)
                throw new Exception();
            int apd_version = (int) stream.nval;

            // p = 0: Actions, p = 1: Predicates
            for (int p = 0; p <= 1; p++)
            {
                stream.nextToken(); // tag
                if (stream.ttype != StreamTokenizer.TT_WORD)
                    throw new Exception("tag");
                stream.nextToken();
                if (stream.ttype != StreamTokenizer.TT_NUMBER)
                    throw new Exception("number of functions");
                int n = (int) stream.nval; // number of functions
                for (int i = 0; i < n; i++)
                {
                    // read and set function name
                    SB_Function func;
                    stream.nextToken();
                    if (stream.ttype != StreamTokenizer.TT_NUMBER)
                        throw new Exception("function name length");
                    int len = (int) stream.nval;
                    in.read(cbuf, 0, len);
                    DefaultMutableTreeNode treeNode;
                    if (p == 0)
                    {
                        func = new SB_Action(new String(cbuf, 0, len));
                        treeNode = new DefaultMutableTreeNode(func);
                        insertNodeInto(treeNode, coreActions, false);
                    } else
                    {
                        func = new SB_Predicate(new String(cbuf, 0, len));
                        treeNode = new DefaultMutableTreeNode(func);
                        insertNodeInto(treeNode, corePredicates, false);
                    }

                    // for predicate, read and set return type
                    if (p == 1)
                    {
                        stream.nextToken();
                        if (stream.ttype != StreamTokenizer.TT_NUMBER)
                            throw new Exception("return type");
                        int retType = (int) stream.nval;
                        ((SB_Predicate) func).setRetType(retType);
                    }

                    stream.nextToken();
                    int m = (int) stream.nval; // number of parameters
                    // iterate through parameters
                    for (int j = 0; j < m; j++)
                    {
                        // read and set parameter name, type, direction
                        stream.nextToken();
                        if (stream.ttype != StreamTokenizer.TT_NUMBER)
                            throw new Exception("param name length");
                        len = (int) stream.nval;
                        in.read(cbuf, 0, len);
                        SB_Parameter param = new SB_Parameter(new String(cbuf, 0, len));
                        stream.nextToken();
                        if (stream.ttype != StreamTokenizer.TT_NUMBER)
                            throw new Exception("type");
                        int type = (int) stream.nval;
                        stream.nextToken();
                        if (stream.ttype != StreamTokenizer.TT_NUMBER)
                            throw new Exception("dir");
                        int direction = (int) stream.nval;
                        param.setType(type);
                        param.setDirection(direction);
                        param.setEditable(false);
                        treeNode.add(new DefaultMutableTreeNode(param));
                        stream.nextToken();
                        if (stream.ttype != StreamTokenizer.TT_NUMBER)
                            throw new Exception("param description length");
                        len = (int) stream.nval;
                        in.read(cbuf, 0, len);
                    }

                    // read and set description
                    stream.nextToken();
                    if (stream.ttype != StreamTokenizer.TT_NUMBER)
                        throw new Exception("description length");
                    len = (int) stream.nval;
                    in.read(cbuf, 0, len);
                    func.setDescription(new String(cbuf, 0, len));

                    // read and set editable flag and ID
                    stream.nextToken();
                    if (stream.ttype != StreamTokenizer.TT_NUMBER)
                        throw new Exception("editable");
                    boolean editable = stream.nval != 0;
                    func.setCore(!editable);
                   
                    stream.nextToken();
                    if (stream.ttype != StreamTokenizer.TT_NUMBER)
                        throw new Exception("id");
                    int id = (int) stream.nval;
                    func.setId(id);

                    stream.nextToken();
                    if (stream.ttype != StreamTokenizer.TT_NUMBER)
                        throw new Exception("k");
                    int k = (int) stream.nval;
                    if (k != 1)
                        throw new Exception("-1");
                }
            }

            in.close();
        } catch (FileNotFoundException exception)
        {
            System.err.println("file not found: base.dat");
        } catch (IOException exception)
        {
            System.err.println("i/o exception");
        }

    }

    protected void newBehaviors()
    {
        _behaviors.removeAllChildren();

        _main = new SB_Behavior("Main");
        _main.setMain(true);
        _main.setEditor(getEditor());
        _behaviors.add(new DefaultMutableTreeNode(_main));

    }

    protected void newGlobals()
    {
        _globals.removeAllChildren();
        SB_Global global = new SB_Global("gEmpty");
        global.setType(SB_Variable.kString);
        global.setPolymorphic(true);
        _globals.add(new DefaultMutableTreeNode(global));
    }

    protected void newConstants()
    {
        _constants.removeAllChildren();
    }
    
    protected void newTypes(){
    	_types.removeAllChildren();
    }

    protected void saveAPD(ObjectOutputStream s)
    {
        _actions.setParent(null);
        _predicates.setParent(null);
        _constants.setParent(null);

        try
        {
            s.writeObject(_actions);
            s.writeObject(_predicates);
            s.writeObject(_constants);
        } catch (IOException exception)
        {
            System.err.println("i/o exception");
        }

        _actions.setParent(_root);
        _predicates.setParent(_root);
        _constants.setParent(_root);
    }

    protected void openAPD(ObjectInputStream s)
    {
        _root.removeAllChildren();

        try
        {
            _actions = (DefaultMutableTreeNode) s.readObject();
            _predicates = (DefaultMutableTreeNode) s.readObject();
            _constants = (DefaultMutableTreeNode) s.readObject();
        } catch (ClassNotFoundException exception)
        {
            System.err.println("openAPD: class not found");
            exception.printStackTrace();
        } catch (IOException exception)
        {
            JOptionPane.showMessageDialog(ComponentRegistry.getFrame(),
                "Unable to load APD file.   ", "I/O Exception", JOptionPane.ERROR_MESSAGE);
            exception.printStackTrace();
            System.exit(1);
        }

        _globals = new DefaultMutableTreeNode("Globals");

        _root.add(_actions);
        _root.add(_predicates);
        _root.add(_globals);
        _root.add(_constants);
    }

    public Enumeration getBehaviors()
    {
        if (_behaviors != null)
            return _behaviors.preorderEnumeration();
        return null;
    }

    protected void saveSBP(ObjectOutputStream s)
    {
        SB_Behavior behavior;
        Vector behaviorNodes = new Vector();
        Enumeration e = _behaviors.preorderEnumeration();
        DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) e.nextElement();
        while (e.hasMoreElements())
        {
            treeNode = (DefaultMutableTreeNode) e.nextElement();
            if (treeNode.getUserObject() instanceof SB_Behavior)
            {
                behavior = (SB_Behavior) treeNode.getUserObject();
                if (behavior.isBTNModified())
                    saveBTN(treeNode);
                behaviorNodes.add(treeNode);
            }
        }

        if (s == null)
            return;

        _behaviors.setParent(null);

        DefaultMutableTreeNode parentNode, tempNode;
        int size = behaviorNodes.size();
        for (int i = 0; i < size; ++i)
        {
            treeNode = (DefaultMutableTreeNode) behaviorNodes.get(i);
            behavior = (SB_Behavior) treeNode.getUserObject();
            parentNode = (DefaultMutableTreeNode) treeNode.getParent();
            parentNode.remove(treeNode);
            tempNode = new DefaultMutableTreeNode(behavior.getName());
            behavior.setTempNode(tempNode);
            parentNode.add(tempNode);
        }

        Vector globalNodes = new Vector();
        size = _globals.getChildCount();
        for (int i = 0; i < size; ++i)
        {
            treeNode = (DefaultMutableTreeNode) _globals.getChildAt(i);
            SB_Global global = (SB_Global) treeNode.getUserObject();
            if (!global.isPolymorphic())
            {
                treeNode.setParent(null);
                globalNodes.add(treeNode);
            }
        }

        try
        {
            s.writeObject(_behaviors);
            s.writeObject(_main.getName());
            s.writeObject(globalNodes);
            s.writeObject(_rootPackage);
        } catch (IOException exception)
        {
            System.err.println("i/o exception");
        }

        size = behaviorNodes.size();
        for (int i = 0; i < size; ++i)
        {
            treeNode = (DefaultMutableTreeNode) behaviorNodes.get(i);
            behavior = (SB_Behavior) treeNode.getUserObject();
            tempNode = behavior.getTempNode();
            parentNode = (DefaultMutableTreeNode) tempNode.getParent();
            parentNode.remove(tempNode);
            behavior.setTempNode(null);
            parentNode.add(treeNode);
        }

        size = globalNodes.size();
        for (int i = 0; i < size; ++i)
        {
            treeNode = (DefaultMutableTreeNode) globalNodes.get(i);
            treeNode.setParent(_globals);
        }

        _behaviors.setParent(_root);
    }

    protected void openSBP(ObjectInputStream s)
    {
        String mainName = null;
        Vector globalNodes = null;
        try
        {
            _behaviors = (DefaultMutableTreeNode) s.readObject();
            mainName = (String) s.readObject();
            globalNodes = (Vector) s.readObject();
            if (SB_ProjectBar.getSBPVersion() >= 5){
            	_rootPackage = (SB_Package) s.readObject();
                // registers classes;
                registerPackage(_rootPackage);
                // updates class map
            	updateClassMap();
            }
        } catch (ClassNotFoundException exception)
        {
            System.err.println("class not found");
            exception.printStackTrace();
        } catch (IOException exception)
        {
            JOptionPane.showMessageDialog(ComponentRegistry.getFrame(),
                "Unable to load SBP file.   ", "I/O Exception", JOptionPane.ERROR_MESSAGE);
            System.exit(1);
        }

        int index = _root.getIndex(_predicates);
        _root.insert(_behaviors, index + 1);

        Vector tempNodes = new Vector();
        Enumeration e = _behaviors.preorderEnumeration();
        DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) e.nextElement();
        while (e.hasMoreElements())
        {
            treeNode = (DefaultMutableTreeNode) e.nextElement();
            if (treeNode.getUserObject() instanceof String)
                tempNodes.add(treeNode);
        }

        DefaultMutableTreeNode parentNode;
        int size = tempNodes.size();
        for (int i = 0; i < size; ++i)
        {
            treeNode = (DefaultMutableTreeNode) tempNodes.get(i);
            parentNode = (DefaultMutableTreeNode) treeNode.getParent();
            parentNode.remove(treeNode);
            // System.out.print("Opening behavior "+treeNode.toString());
            openBTN(treeNode.toString(), parentNode);
        }

        _main = findBehavior(mainName);
        if (_main == null)
        {
            JOptionPane.showMessageDialog(ComponentRegistry.getFrame(),
                "New main behavior will be inserted.   ", "Missing Main Behavior",
                JOptionPane.WARNING_MESSAGE);
            _main = new SB_Behavior("Main");
            _main.setMain(true);
            _main.setEditor(this.getEditor());
            _main.getPoly(0)._indices = ComponentRegistry.getProjectBar()._descriptors
                    .getBasePolyIndices();
            _main.setNameToNextAvailable(treeNode);
            DefaultMutableTreeNode mainNode = new DefaultMutableTreeNode(_main);
            insertNodeInto(mainNode, _behaviors);
        }
        updateComboBehav();
        SB_TabbedCanvas tabbedCanvas = ComponentRegistry.getContent();
        tabbedCanvas._behavior = null;
        tabbedCanvas.setBehavior(_main, false);

        size = globalNodes.size();
        for (int i = 0; i < size; ++i)
            _globals.add((DefaultMutableTreeNode) globalNodes.get(i));

        // adds types;
        _root.add(_types);
        updateTypes();

        DefaultTreeModel treeModel = (DefaultTreeModel) this.treeModel;
        treeModel.reload();
    }

    protected void saveBTN(DefaultMutableTreeNode treeNode)
    {
        DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) treeNode.getParent();
        treeNode.setParent(null);

        try
        {
            File fileBTN = new File(ComponentRegistry.getProjectBar()._fileSBP.getParent()
                    + /* File.separator */"//" + treeNode.toString() + ".btn");
            FileOutputStream out = new FileOutputStream(fileBTN);
            ObjectOutputStream s = new ObjectOutputStream(out);
            // save BTN version number
            s.writeInt(SB_ProjectBar.BTN_VER);
            s.writeObject(treeNode);
            s.flush();
            out.close();
        } catch (IOException exeption)
        {
            System.err.println("i/o exception");
        }

        treeNode.setParent(parentNode);
    }

    protected void openBTN(String name, DefaultMutableTreeNode parentNode)
    {
        String pathname = ComponentRegistry.getProjectBar()._fileSBP.getParent()
                + File.separator + name + ".btn";
        File fileBTN = new File(pathname);
        openBTN(fileBTN,parentNode);
    }
    
    protected SB_Behavior openBTN(File fileBTN, DefaultMutableTreeNode parentNode)
    {
        DefaultMutableTreeNode treeNode = null;
        SB_Behavior beh = null;
        try
        {
            FileInputStream in = new FileInputStream(fileBTN);
            ObjectInputStream s = new ObjectInputStream(in);
            // open BTN version number
            SB_ProjectBar.setBTNVersion(s.readInt());
            // check if BTN version too new
            if (SB_ProjectBar.getBTNVersion() > SB_ProjectBar.BTN_VER)
            {
                // warn user and abort
                JOptionPane.showMessageDialog(ComponentRegistry.getFrame(),
                    "SimBionic must be updated to open  \n" + "'" + fileBTN.getPath() + "'.",
                    "Too New BTN Version", JOptionPane.ERROR_MESSAGE);
                System.exit(1);
            }
            treeNode = (DefaultMutableTreeNode) s.readObject();
            beh = (SB_Behavior) treeNode.getUserObject();
            beh.setEditor(getEditor());
                        
            parentNode.add(treeNode);
            in.close();
        } catch (FileNotFoundException exception)
        {
            JOptionPane.showMessageDialog(ComponentRegistry.getFrame(), "'" + fileBTN.getPath()
                    + "' cannot be found.   ", "Missing BTN File", JOptionPane.WARNING_MESSAGE);
        } catch (IOException exception)
        {
            JOptionPane.showMessageDialog(ComponentRegistry.getFrame(), "Unable to load '"
                    + fileBTN.getPath() + "'.", "I/O Exception", JOptionPane.ERROR_MESSAGE);
            System.exit(1);
        } catch (ClassNotFoundException exception)
        {
            System.err.println("class not found");
            exception.printStackTrace();
        }
        
        ComponentRegistry.getOutputBar().getBuild().addLine(new SB_Line("Loaded '" + fileBTN.getPath() + "'"));
        
        return beh;
    }

    protected void mouseRightPressed(DefaultMutableTreeNode treeNode, int x, int y)
    {
        if (treeNode == _actions || treeNode == _predicates || treeNode == _behaviors)
        {
            _insertActionItem.setVisible(false);
            _insertPredicateItem.setVisible(false);
            _insertBehaviorItem.setVisible(false);
            _importBehaviorItem.setVisible(false);
            _duplicateBehaviorItem.setVisible(false);
            _separator.setVisible(false);
            _renameFolderItem.setVisible(false);
            _deleteFolderItem.setVisible(false);

            if (treeNode == _actions)
                _insertActionItem.setVisible(true);
            else if (treeNode == _predicates)
                _insertPredicateItem.setVisible(true);
            else
            {
                _insertBehaviorItem.setVisible(true);
                _importBehaviorItem.setVisible(true);
            }                

            _functionsPopup.show(this, x, y);
        } else if (treeNode == _constants)
            _constantsPopup.show(this, x, y);
        else if (treeNode == _globals)
            _globalsPopup.show(this, x, y);
        else if (treeNode == _types)
        	_typesPopup.show(this, x, y);
    }

    protected void mouseRightPressed(UserObject userObject, int x, int y)
    {
        boolean editable = userObject.isCellEditable();
        _importBehaviorItem.setVisible(false);
        _duplicateBehaviorItem.setVisible(false);

        if (userObject instanceof SB_Function)
        {      
            if (userObject instanceof SB_Action)
            {
                _retTypeSubmenu.setVisible(false);
                _deleteFunctionItem.setEnabled(editable);
                _execSubmenu.setVisible(false);
                _interruptSubmenu.setVisible(false);
                _descriptionSeparator.setVisible(false);
            } else if (userObject instanceof SB_Predicate)
            {
                SB_VarType type = SB_TypeManager.convertOldTypeId(((SB_Predicate)userObject).getRetType());
                _retTypeItems[_typeManager.idToReturnValueComboIndex(type, false)].setSelected(true);
//                _retTypeItems[((SB_Predicate) userObject).getRetType()].setSelected(true);
                setItemsEnabled(_retTypeItems, editable);
                _retTypeSubmenu.setVisible(true);
                _deleteFunctionItem.setEnabled(editable);
                _execSubmenu.setVisible(false);
                _interruptSubmenu.setVisible(false);
                _descriptionSeparator.setVisible(false);
            } else if (userObject instanceof SB_Behavior)
            {
                _retTypeSubmenu.setVisible(false);
                _deleteFunctionItem.setEnabled(editable && userObject != _main);
                SB_Behavior behavior = ((SB_Behavior) userObject);
                int exec = behavior.getExec();
                _execItems[SB_Behavior.execToIndex(exec)].setSelected(true);
                setItemsEnabled(_execItems, editable);
                _execSubmenu.setVisible(true);
                int interrupt = behavior.getInterrupt();
                _interruptItems[SB_Behavior.interruptToIndex(interrupt)].setSelected(true);
                setItemsEnabled(_interruptItems, editable);
                _interruptSubmenu.setVisible(true);
                _descriptionSeparator.setVisible(true);
                _duplicateBehaviorItem.setVisible(true);
            }
            _insertParameterItem.setEnabled(editable);
            _renameFunctionItem.setEnabled(editable);
            if (SimBionicEditor.DEV)
            {
                if (userObject instanceof SB_Behavior)
                {
                    _reservedSeparator.setVisible(false);
                    _reservedItem.setVisible(false);
                } else
                {
                    boolean core = ((SB_Function) userObject).isCore();
                    _reservedItem.setSelected(core);
                    _reservedSeparator.setVisible(true);
                    _reservedItem.setVisible(true);
                }
            }
            _functionPopup.show(this, x, y);
        } else if (userObject instanceof SB_Folder)
        {
            TreePath treePath = getSelectionPath();
            DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) treePath
                    .getLastPathComponent();
            _insertActionItem.setVisible(treeNode.isNodeAncestor(_actions));
            _insertPredicateItem.setVisible(treeNode.isNodeAncestor(_predicates));
            _insertBehaviorItem.setVisible(treeNode.isNodeAncestor(_behaviors));
            _importBehaviorItem.setVisible(treeNode.isNodeAncestor(_behaviors));
            _separator.setVisible(true);
            _renameFolderItem.setVisible(true);
            _deleteFolderItem.setVisible(true);
            _renameFolderItem.setEnabled(editable);
            _deleteFolderItem.setEnabled(editable);
            _functionsPopup.show(this, x, y);
        } else if (userObject instanceof SB_Variable)
        {
            _renameVariableItem.setEnabled(editable);
            _deleteVariableItem.setEnabled(editable);
            SB_VarType type = SB_TypeManager.convertOldTypeId(((SB_Variable)userObject).getType());

            setItemsEnabled(_directionItems, editable);
            TreePath treePath = getSelectionPath();
            int count = treePath.getPathCount();
            DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) treePath
                    .getLastPathComponent();
            DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) treePath
                    .getPathComponent(count - 2);
            int index = treeModel.getIndexOfChild(parentNode, treeNode);
            _moveUpItem.setEnabled(editable && index > 0);
            _moveDownItem.setEnabled(editable && index < treeModel.getChildCount(parentNode) - 1);


            if (userObject instanceof SB_Parameter)
            {
                _directionItems[((SB_Parameter) userObject).getDirection()].setSelected(true);
                _directionSubmenu.setVisible(true);
                _valueItem.setVisible(false);
                _initialValueItem.setVisible(false);
            	clearTypeSubMenu();
                populateTypeSubMenu(false, true);
                _typeItems[_typeManager.idToVarComboIndex(type, false, true)].setSelected(true);
                setItemsEnabled(_typeItems, editable);

            } else if (userObject instanceof SB_Constant)
            {
                _directionSubmenu.setVisible(false);

                _valueItem.setVisible(true);
                _initialValueItem.setVisible(false);
            	clearTypeSubMenu();
                populateTypeSubMenu(true, false);
                _typeItems[_typeManager.idToVarComboIndex(type, true, false)].setSelected(true);
                setItemsEnabled(_typeItems, editable);
            } else if (userObject instanceof SB_Global)
            {
                _directionSubmenu.setVisible(false);
                _valueItem.setVisible(false);
                _initialValueItem.setVisible(true);
                _initialValueItem.setEnabled(!type.equals(SB_VarType.kData) && !type.equals(SB_VarType.kEntity));

            	clearTypeSubMenu();
                populateTypeSubMenu(false, false);
                _typeItems[_typeManager.idToVarComboIndex(type)].setSelected(true);
                setItemsEnabled(_typeItems, editable);

                SB_Global global = (SB_Global) userObject;
                if (global.isPolymorphic())
                {
                    return;
                } else
                {
                    DefaultMutableTreeNode siblingNode =  treeNode
                            .getPreviousSibling();
                    SB_Global siblingGlobal = (SB_Global) siblingNode.getUserObject();
                    if (siblingGlobal.isPolymorphic())
                        _moveUpItem.setEnabled(false);
                }
            }
            else
            {
            	clearTypeSubMenu();
                populateTypeSubMenu(false, false);
                _typeItems[_typeManager.idToVarComboIndex(type)].setSelected(true);
                setItemsEnabled(_typeItems, editable);
            }
            _variablePopup.show(this, x, y);
        }
        else if (userObject instanceof SB_Package)
        	_packagePopup.show(this, x, y);
        else if (userObject instanceof SB_Class)
        	_classPopup.show(this, x, y);
    }

    public void actionPerformed(ActionEvent e)
    {
        JComboBox comboBehav = _comboBehav;
        if (comboBehav != null && e.getSource() == comboBehav)
        {
            if (comboBehav.getSelectedItem() == ComponentRegistry.getContent()._behavior)
            {
                SB_Canvas canvas = ComponentRegistry.getContent().getActiveCanvas();
                if (canvas == null)
                    return;
                canvas.requestFocus();
                return;
            }
            JTextField textField = (JTextField) comboBehav.getEditor().getEditorComponent();
            String text = textField.getText();

            SB_Behavior behavior;
            int size = comboBehav.getItemCount();
            for (int i = 0; i < size; ++i)
            {
                behavior = (SB_Behavior) comboBehav.getItemAt(i);
                if (behavior.compareTo(text) == 0)
                {
                    if (!ComponentRegistry.getContent().setBehavior(behavior, true))
                    {
                        comboBehav.setSelectedItem(behavior);
                        ComponentRegistry.getContent().getActiveCanvas().requestFocus();
                    }
                    return;
                }
            }

            String name;
            int length = text.length();
            for (int i = 0; i < size; ++i)
            {
                behavior = (SB_Behavior) comboBehav.getItemAt(i);
                name = behavior.toString();
                if (name.length() >= length
                        && name.substring(0, length).compareToIgnoreCase(text) == 0)
                {
                    if (!ComponentRegistry.getContent().setBehavior(behavior, true))
                    {
                        comboBehav.setSelectedItem(behavior);
                        ComponentRegistry.getContent().getActiveCanvas().requestFocus();
                    }
                    return;
                }
            }

            comboBehav.setSelectedItem(ComponentRegistry.getContent()._behavior);
            if (ComponentRegistry.getContent().getActiveCanvas() != null)
                ComponentRegistry.getContent().getActiveCanvas().requestFocus();
            //Toolkit.getDefaultToolkit().beep();
        } else if (e.getSource() instanceof JComponent)
        {
            JComponent component = (JComponent) e.getSource();
            TreePath treePath = getSelectionPath();
            DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) treePath
                    .getLastPathComponent();
            Object userObject = treeNode.getUserObject();
            if (component == _descriptionOK)
            {
                SB_Function function = (SB_Function) userObject;
                if (!function.getDescription().equals(_descriptionTextArea.getText()))
                {
                    function.setDescription(_descriptionTextArea.getText());
                    if (function instanceof SB_Behavior)
                        ((SB_Behavior) function).setBTNModified(true);
                    else
                        setAPDModified(true);
                }
                _descriptionDialog.setVisible(false);
            } else if (component == _descriptionCancel)
            {
                _descriptionDialog.setVisible(false);
            } else if (component == _constantValueOK || component == _constantValueTextField)
            {
                SB_Constant constant = (SB_Constant) userObject;
                if (!constant.getValue().equals(_constantValueTextField.getText()))
                {
                    constant.setValue(_constantValueTextField.getText());
                    setAPDModified(true);
                }
                _constantValueDialog.setVisible(false);
            } else if (component == _constantValueCancel)
            {
                _constantValueDialog.setVisible(false);
            } else if (component == _initialValueOK || component == _initialValueTextField)
            {
                SB_Global global = (SB_Global) userObject;
                if (!global.getInitial().equals(_initialValueTextField.getText()))
                {
                    global.setInitial(_initialValueTextField.getText());
                    setSBPModified(true);
                }
                _initialValueDialog.setVisible(false);
            } else if (component == _initialValueCancel)
            {
                _initialValueDialog.setVisible(false);
            } 
            else if (component == _editClassIdOK || component == _classIdField)
            {
                SB_Class theClass = (SB_Class) userObject;
                if (!Integer.toString(theClass.getTypeId().getState()).equals(_classIdField.getText()))
                {
                    if (!changeClassTypeId(theClass,Integer.parseInt(_classIdField.getText())))
                    {
                        JOptionPane.showMessageDialog(_editClassIdDialog,"The specified type ID is already in use by another class.","Invalid Type ID",JOptionPane.ERROR_MESSAGE);
                        return;
                    }
                    setSBPModified(true);
                }
                _editClassIdDialog.setVisible(false);                
            }
            else if (component == _editClassIdCancel)
            {
                _editClassIdDialog.setVisible(false);
            }
            else
                super.actionPerformed(e);
        }
    }

    public void insertNewPredicate(String name)
    {
        insertPredicate(_predicates, name);
        this.setSelectionPath(null);
    }

    protected void addOutParameterToPredicate(DefaultMutableTreeNode treeNode, String name)
    {
        SB_Parameter param = new SB_Parameter(name);
        param.setDirection(SB_Parameter.kInOut);
    }

    protected void insertPredicate(DefaultMutableTreeNode treeNode, String name)
    {
        UserObject childUserObject = new SB_Predicate(name);
        ((SB_Predicate) childUserObject).setId(findUniqueId(_predicates, START_USER_ID));
        setAPDModified(true);
        childUserObject.setNameToNextAvailable(treeNode);
        DefaultMutableTreeNode childNode = new DefaultMutableTreeNode(childUserObject);
        insertNodeInto(childNode, treeNode);
        if (childUserObject instanceof SB_Behavior)
            ComponentRegistry.getContent().setBehavior((SB_Behavior) childUserObject, true);
        else if (childUserObject instanceof SB_Variable)
            setVariableModified(childNode);
        startEditingAtPath(new TreePath(childNode.getPath()));
    }

    public SB_Constant insertConstant(String folderName, String constantName)
    {
        SB_Constant constant = new SB_Constant(constantName);
        if (folderName == null)
        {
            addToCatalog(_constants, constant, false);
        } 
        else
        {
            DefaultMutableTreeNode folder = this.findNodeByName(folderName, _constants);
            if (folder != null && folder.getUserObject() instanceof SB_Folder)
            {
                addToCatalog(folder, constant, false);
            }
        }
        return constant;
    }
    
    public SB_Behavior insertBehavior(String folderName, String behaviorName)
    {
        SB_Behavior behavior = null;
        if (folderName == null)
        {
            behavior=insertBehavior(_behaviors, behaviorName, false);
        } else
        {
            DefaultMutableTreeNode folder = this.findNodeByName(folderName, _behaviors);
            if (folder != null && folder.getUserObject() instanceof SB_Folder)
            {
                behavior=insertBehavior(folder, behaviorName, false);
            }
        }
        return behavior;
    }

    private SB_Behavior insertBehavior(DefaultMutableTreeNode parentNode, String name,
            boolean editNode)
    {
        SB_Behavior behavior = new SB_Behavior(name);
        behavior.getPoly(0)._indices = ComponentRegistry.getProjectBar()._descriptors.getBasePolyIndices();
        addBehavior(behavior,parentNode,editNode);
        return behavior;
    }
    
    /**
     * Initializes the given behavior and adds it to the tree.
     * @param newBehavior
     * @param parentNode
     * @param editNode true if the new node should start in edit mode
     */
    private DefaultMutableTreeNode addBehavior(SB_Behavior newBehavior,DefaultMutableTreeNode parentNode,boolean editNode)
    {
        String name = newBehavior.getName();
        newBehavior.setNameToNextAvailable(this._root);
        newBehavior.setEditor(getEditor());
        insertToComboBehav(newBehavior);
        newBehavior.setBTNModified(true);
        setSBPModified(true);
        return addToCatalog(parentNode, newBehavior, editNode);        
    }

    private DefaultMutableTreeNode addToCatalog(DefaultMutableTreeNode parentNode, UserObject childUserObject,
            boolean editNode)
    {
        childUserObject.setNameToNextAvailable(parentNode);
        DefaultMutableTreeNode childNode = new DefaultMutableTreeNode(childUserObject);
        insertNodeInto(childNode, parentNode);
        if (childUserObject instanceof SB_Behavior)
            ComponentRegistry.getContent().setBehavior((SB_Behavior) childUserObject, true);
        else if (childUserObject instanceof SB_Variable)
            setVariableModified(childNode);
        if (editNode)
            startEditingAtPath(new TreePath(childNode.getPath()));
        return childNode;
    }

    protected void handleMenuItem(JMenuItem menuItem, TreePath treePath)
    {
        DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) treePath.getLastPathComponent();
        Object userObject = treeNode.getUserObject();

        UserObject childUserObject = null;
        if (menuItem == _insertActionItem)
        {
            childUserObject = new SB_Action("NewAction");
            ((SB_Action) childUserObject).setId(findUniqueId(_actions, START_USER_ID));
            setAPDModified(true);
        } else if (menuItem == _insertPredicateItem)
        {
            insertPredicate(treeNode, "NewPredicate");
        } else if (menuItem == _insertBehaviorItem)
        {
            insertBehavior(treeNode, "NewBehavior", true);
        } else if (menuItem == _importBehaviorItem)
        {
            importBehavior(treeNode);
        } else if (menuItem == _duplicateBehaviorItem)
        {
            duplicateBehavior(treeNode);
        } else if (menuItem == _newFolderItem)
        {
            childUserObject = new SB_Folder("NewFolder");
            setFolderModified(treeNode);
        } else if (menuItem == _insertConstantItem)
        {
            childUserObject = new SB_Constant("NewConstant");
        } else if (menuItem == _insertGlobalItem)
        {
            childUserObject = new SB_Global("NewGlobal");
        } else if (menuItem == _insertParameterItem)
        {
            childUserObject = new SB_Parameter("NewParameter");
        }
        if (childUserObject != null)
        {
            addToCatalog(treeNode, childUserObject, true);
            return;
        }

        DefaultTreeModel treeModel = (DefaultTreeModel) this.treeModel;
        int index = -1;
        if (menuItem == _renameFolderItem || menuItem == _renameFunctionItem
                || menuItem == _renameVariableItem)
        {
            startEditingAtPath(treePath);
        } else if (menuItem == _deleteFolderItem || menuItem == _deleteFunctionItem
                || menuItem == _deleteVariableItem)
        {
            deleteNode(treeNode, true);
        } else if ((index = findItemIndex(menuItem, _retTypeItems)) != -1)
        {
            SB_Predicate predicate = (SB_Predicate) userObject;
        	SB_VarType type = _typeManager.returnValueComboIndexToId(index, false);
            int id = _typeManager.convertToOldId(type);
            if (predicate.getRetType() != id)
            {
                predicate.setRetType(id);
                treeModel.nodeChanged(treeNode);
                setAPDModified(true);
            }
        } else if ((index = findItemIndex(menuItem, _execItems)) != -1)
        {
            SB_Behavior behavior = (SB_Behavior) userObject;
            int exec = behavior.getExec();
            if (SB_Behavior.execToIndex(exec) != index)
            {
                behavior.setExec(SB_Behavior.indexToExec(index));
                behavior.setBTNModified(true);
            }
        } else if ((index = findItemIndex(menuItem, _interruptItems)) != -1)
        {
            SB_Behavior behavior = (SB_Behavior) userObject;
            int interrupt = behavior.getInterrupt();
            if (SB_Behavior.interruptToIndex(interrupt) != index)
            {
                behavior.setInterrupt(SB_Behavior.indexToInterrupt(index));
                behavior.setBTNModified(true);
            }
        } else if (menuItem == _descriptionItem)
        {
            showDescriptionDialog((SB_Function) userObject);
        } else if (menuItem == _reservedItem)
        {
            SB_Function function = (SB_Function) userObject;
            function.setCore(!function.isCore());
            int size = treeNode.getChildCount();
            for (int i = 0; i < size; ++i)
            {
                DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) treeNode.getChildAt(i);
                ((SB_Variable) childNode.getUserObject()).setEditable(!function.isCore());
            }
            if (function instanceof SB_Behavior)
            {
                SB_TabbedCanvas tabbedCanvas = ComponentRegistry.getContent();
                if (tabbedCanvas._behavior == function)
                {
                    SB_Canvas canvas = tabbedCanvas.getActiveCanvas();
                    canvas.clearSingle();
                    canvas.updateSingle();
                    // _editor.updateTitle();
                }
                ((SB_Behavior) function).updateLocalsEditable();
                ((SB_Behavior) function).setBTNModified(true);
            } else
            {
                int startId = function.isCore() ? 0 : START_USER_ID;
                if (function instanceof SB_Action)
                    function.setId(findUniqueId(_actions, startId));
                else
                    function.setId(findUniqueId(_predicates, startId));
                setAPDModified(true);
            }
        } else if ((index = findItemIndex(menuItem, _typeItems)) != -1)
        {
            SB_Variable var = (SB_Variable) userObject;
            SB_VarType type = null;
            if (var instanceof SB_Parameter)
            	type = _typeManager.varComboIndexToId(index, false, true);
            else if (var instanceof SB_Constant)
            	type = _typeManager.varComboIndexToId(index, true, false);
            else
            	type = _typeManager.varComboIndexToId(index, false, false);
            int id = _typeManager.convertToOldId(type);
            if (var.getType() != id)
            {
                var.setType(id);
                treeModel.nodeChanged(treeNode);
                setVariableModified(treeNode);
            }
        } else if ((index = findItemIndex(menuItem, _directionItems)) != -1)
        {
            SB_Parameter param = (SB_Parameter) userObject;
            if (param.getDirection() != index)
            {
                param.setDirection(index);
                treeModel.nodeChanged(treeNode);
                setVariableModified(treeNode);
            }
        } else if (menuItem == _valueItem)
        {
            showConstantValueDialog((SB_Constant) userObject);
        } else if (menuItem == _initialValueItem /*
                                                     * || menuItem ==
                                                     * _initialValueItemPG
                                                     */)
        {
            showInitialValueDialog((SB_Global) userObject);
        } else if (menuItem == _moveUpItem || menuItem == _moveDownItem)
        {
            int count = treePath.getPathCount();
            DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) treePath
                    .getPathComponent(count - 2);
            index = treeModel.getIndexOfChild(parentNode, treeNode);
            treeModel.removeNodeFromParent(treeNode);
            if (menuItem == _moveUpItem)
                treeModel.insertNodeInto(treeNode, parentNode, index - 1);
            else
                treeModel.insertNodeInto(treeNode, parentNode, index + 1);
            setSelectionPath(new TreePath(treeNode.getPath()));
            setVariableModified(treeNode);
        }
        else if (menuItem == _importClassSpecItem)
        {
            getImportClassSpecDialog().setVisible(true);
        }
        else if (menuItem == _classSpecGeneratorItem)
        {
            getClassSpecGeneratorDialog().setVisible(true);
        }
        else if (menuItem == _editClassIdItem)
        {
            editClassId((SB_Class)treeNode.getUserObject());
        }
        else if (menuItem == _removePackageItem)
        {
        	removePackageOrClass(treeNode, (DefaultMutableTreeNode)treeNode.getParent(), _rootPackage);
        }
        else if (menuItem == _removeClassItem)
        {
        	DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) treeNode.getParent();
        	SB_Package parentPackage = (SB_Package) parentNode.getUserObject();
        	removePackageOrClass(treeNode, parentNode, parentPackage);
        }

    }

    public void importBehavior(String folderName, String behaviorFileName)
    {
        if (folderName == null)
        {
        	importBehavior(_behaviors, new File(behaviorFileName), true);
        } 
        else
        {
			DefaultMutableTreeNode folder = this.findNodeByName(folderName, _behaviors);
	        if (folder != null && folder.getUserObject() instanceof SB_Folder)
	        {
	            importBehavior(folder, new File(behaviorFileName), true);
	        }
        }
    }
    
    /**
     * Loads the specified behavior file.  If recursive is true, also attempts to load any behaviors
     * referenced by the imported behavior (if they are not already loaded).
     * @param parent parent node of the behavior in the catalog tree
     * @param behaviorFile btn file to load
     * @param recursive if true, loads referenced behaviors as well
     */
    private void importBehavior(DefaultMutableTreeNode parent, File behaviorFile, boolean recursive)
    {
        importBehaviorHelper(parent,behaviorFile,recursive);
        ((DefaultTreeModel)treeModel).reload();
        setSBPModified(true);
    }
    
    private void importBehaviorHelper(DefaultMutableTreeNode parent, File behaviorFile, boolean recursive)
    {
        SB_Behavior behavior = openBTN(behaviorFile,parent);
        if (behavior == null)
            return;
        
        insertToComboBehav(behavior);
        behavior.setBTNModified(true);
        
        if (recursive)
        {
            String path = behaviorFile.getParentFile().getPath();
            
            Set referencedBehaviors = behavior.getReferencedBehaviors();
            Iterator behIt = referencedBehaviors.iterator();
            while (behIt.hasNext())
            {
                String behName = (String)behIt.next();
                if ((findBehavior(behName) == null) && (findAction(behName) == null)) 
                {
                    // referenced behavior is not in project, so import it too
                    importBehaviorHelper(parent, new File(path + File.separator + behName + ".btn"), true);
                }
            }
        }
    }
    
    /**
     * Imports an existing behavior into the project.
     * @param treeNode the catalog node under which to place the imported behavior
     */
    private void importBehavior(DefaultMutableTreeNode treeNode)
    {
        JFileChooser chooser = new JFileChooser();
        chooser.setFileFilter(new javax.swing.filechooser.FileFilter() {
            public String getDescription()
            {
                return "SimBionic Behavior (*.btn)";
            }
            public boolean accept(File f)
            {
                if (f.isDirectory())
                    return true;
                if (f.getName().indexOf(".btn") != -1)
                    return true;
                return false;
            }
        });
        
        int result = chooser.showOpenDialog(null);

        if (result == JFileChooser.APPROVE_OPTION) 
        {
            File file = chooser.getSelectedFile();
            importBehavior(treeNode, file, true);
        }
    }
    
    /**
     * Duplicates the selected behavior, placing a copy in the Catalog.
     * @param behaviorNode the behavior to copy
     */
    private void duplicateBehavior(DefaultMutableTreeNode behaviorNode)
    {
        SB_Behavior origBehavior = (SB_Behavior)behaviorNode.getUserObject();
        SB_Behavior copyBehavior = (SB_Behavior)origBehavior.clone();
        
        DefaultMutableTreeNode copyNode = addBehavior(copyBehavior,(DefaultMutableTreeNode)behaviorNode.getParent(),true);
        
        // now clone the parameters
        for (int i=0; i < behaviorNode.getChildCount(); ++i)
        {
            DefaultMutableTreeNode paramNode = (DefaultMutableTreeNode)behaviorNode.getChildAt(i);
            SB_Parameter copyParam = (SB_Parameter)((SB_Parameter)paramNode.getUserObject()).clone();
            addToCatalog(copyNode,copyParam,false);
        }
    }

    public void deleteNode(DefaultMutableTreeNode treeNode, boolean checkFolder)
    {
        Object userObject = treeNode.getUserObject();

        SB_TabbedCanvas tabbedCanvas = ComponentRegistry.getContent();
        if (userObject instanceof SB_Folder)
        {
            if (checkFolder && !shouldDeleteFolder(treeNode))
                return;
            if (treeNode.isNodeAncestor(_behaviors))
            {
                if (findNode(tabbedCanvas._behavior, _behaviors).isNodeAncestor(treeNode))
                    tabbedCanvas.setBehavior(_main, true);
                SB_Behavior behavior = null;
                Enumeration e = treeNode.preorderEnumeration();
                DefaultMutableTreeNode next = (DefaultMutableTreeNode) e.nextElement();
                ;
                while (e.hasMoreElements())
                {
                    next = (DefaultMutableTreeNode) e.nextElement();
                    if (next.getUserObject() instanceof SB_Behavior)
                    {
                        behavior = (SB_Behavior) next.getUserObject();
                    }
                }
            }
            setSBPModified(true);
        } else if (userObject instanceof SB_Behavior)
        {
            SB_Behavior behavior = (SB_Behavior) userObject;
            if (tabbedCanvas._behavior == behavior)
                tabbedCanvas.setBehavior(_main, true);
            setSBPModified(true);
        } else if (userObject instanceof SB_Function)
            setAPDModified(true);
        else if (userObject instanceof SB_Variable)
            setVariableModified(treeNode);
        DefaultTreeModel treeModel = (DefaultTreeModel) getModel();
        treeModel.removeNodeFromParent(treeNode);
        if (userObject instanceof SB_Behavior)
        {
            SB_Canvas canvas = tabbedCanvas.getActiveCanvas();
            canvas._poly.getElements().updateComplex(getEditor());
            canvas.repaint();
        }
    }

    protected boolean shouldDeleteFolder(DefaultMutableTreeNode folderNode)
    {
        if (folderNode.isLeaf())
            return true;
        else if (folderNode.isNodeAncestor(_behaviors)
                && findNode(_main, _behaviors).isNodeAncestor(folderNode))
        {
            JOptionPane.showMessageDialog(ComponentRegistry.getFrame(),
                "Folder contains main behavior.   ", "Cannot Delete", JOptionPane.WARNING_MESSAGE);
            return false;
        } else
        {
            Enumeration e = folderNode.preorderEnumeration();
            DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) e.nextElement();
            while (e.hasMoreElements())
            {
                treeNode = (DefaultMutableTreeNode) e.nextElement();
                if (treeNode.getUserObject() instanceof SB_Function)
                {
                    SB_Function function = (SB_Function) treeNode.getUserObject();
                    if (function.isCore() && !SimBionicEditor.DEV)
                    {
                        JOptionPane.showMessageDialog(ComponentRegistry.getFrame(),
                            "Folder contains reserved function.   ", "Cannot Delete",
                            JOptionPane.WARNING_MESSAGE);
                        return false;
                    }
                }
            }
        }
        int n = JOptionPane.showConfirmDialog(ComponentRegistry.getFrame(),
            "Delete folder and its contents?   ", null, JOptionPane.YES_NO_OPTION);
        return n == JOptionPane.YES_OPTION;
    }

   
    public int findUniqueId(DefaultMutableTreeNode parentNode, int startId)
    {
        TreeSet ids = new TreeSet();
        SB_Function function;
        Enumeration e = parentNode.preorderEnumeration();
        DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) e.nextElement();
        while (e.hasMoreElements())
        {
            treeNode = (DefaultMutableTreeNode) e.nextElement();
            if (treeNode.getUserObject() instanceof SB_Function)
            {
                function = (SB_Function) treeNode.getUserObject();
                ids.add(new Integer(function.getId()));
            }
        }

        while (true)
        {
            if (!ids.contains(new Integer(startId)))
            {
                // System.out.println("uniqueId = " + startId);
                return startId;
            }
            ++startId;
        }
    }

    static int findItemIndex(JMenuItem menuItem, JMenuItem[] menuItems)
    {
    	if (menuItems != null){
            int length = menuItems.length;
            for (int i = 0; i < length; ++i)
            {
                if (menuItems[i] == menuItem)
                    return i;
            }
    	}
        return -1;
    }

    static void setItemsEnabled(JMenuItem[] menuItems, boolean enabled)
    {
        int length = menuItems.length;
        for (int i = 0; i < length; ++i)
            menuItems[i].setEnabled(enabled);
    }

    protected void mouseDoubleClicked(UserObject userObject, boolean shiftPressed)
    {
        if (userObject instanceof SB_Function)
        {
            if (userObject instanceof SB_Behavior)
            {
                if (ComponentRegistry.getContent().setBehavior((SB_Behavior) userObject, true))
                    return;
            }
            showDescriptionDialog((SB_Function) userObject);
        } else if (userObject instanceof SB_Constant)
        {
            showConstantValueDialog((SB_Constant) userObject);
        } else if (userObject instanceof SB_Global)
        {
            showInitialValueDialog((SB_Global) userObject);
        }
    }

    protected void mouseTripleClicked(UserObject userObject, boolean shiftPressed)
    {
        super.mouseTripleClicked(userObject, shiftPressed);
        _holdDrag = true;
    }

    static ImageIcon getProjectIcon()
    {
        if (_projectIcon == null)
            _projectIcon = Util.getImageIcon("Project.gif");
        return _projectIcon;
    }

    static ImageIcon getHeadingIcon()
    {
        if (_headingIcon == null)
            _headingIcon = Util.getImageIcon("Heading.gif");
        return _headingIcon;
    }

    protected Icon getIcon(DefaultMutableTreeNode treeNode)
    {
        DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) treeNode.getParent();
        if (treeNode == _root)
        {
            return getProjectIcon();
        } else if (parentNode == _root)
        {
            return getHeadingIcon();
        } else
            return null;
    }

    protected void nodeRenamed(DefaultMutableTreeNode node, String oldName)
    {
        Object userObject = node.getUserObject();
        if (userObject instanceof SB_Folder)
            setFolderModified(node);
        if (userObject instanceof SB_Behavior)
        {
            SB_Canvas canvas = ComponentRegistry.getContent().getActiveCanvas();
            canvas._poly.getElements().updateComplex(getEditor());
            canvas.repaint();

            insertToComboBehav((SB_Behavior) userObject);
            ((SB_Behavior) userObject).setBTNModified(true);
            setSBPModified(true);
        } else if (userObject instanceof SB_Function)
            setAPDModified(true);
        else if (userObject instanceof SB_Variable)
            setVariableModified(node);
    }

    protected void setVariableModified(DefaultMutableTreeNode treeNode)
    {
        SB_Variable userObject = (SB_Variable) treeNode.getUserObject();
        Object parentUserObject = ((DefaultMutableTreeNode) treeNode.getParent()).getUserObject();
        if (userObject instanceof SB_Parameter)
        {
            if (parentUserObject instanceof SB_Behavior)
                ((SB_Behavior) parentUserObject).setBTNModified(true);
            else
                setAPDModified(true);
        } else if (userObject instanceof SB_Global)
            setSBPModified(true);
        else
            setAPDModified(true);
    }

    protected void setFolderModified(DefaultMutableTreeNode treeNode)
    {
        if (treeNode.isNodeAncestor(_behaviors))
            setSBPModified(true);
        else
            setAPDModified(true);
    }

    public SB_Function findFunction(String name)
    {
        SB_Function func;
        Enumeration e = _root.preorderEnumeration();
        DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) e.nextElement();
        while (e.hasMoreElements())
        {
            treeNode = (DefaultMutableTreeNode) e.nextElement();
            if (treeNode.getUserObject() instanceof SB_Function)
            {
                func = (SB_Function) treeNode.getUserObject();
                if (func.getName().equals(name))
                    return func;
            }
        }
        return null;
    }

    public DefaultMutableTreeNode findFuncNode(String name)
    {
        SB_Function func;
        Enumeration e = _root.preorderEnumeration();
        DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) e.nextElement();
        while (e.hasMoreElements())
        {
            treeNode = (DefaultMutableTreeNode) e.nextElement();
            if (treeNode.getUserObject() instanceof SB_Function)
            {
                func = (SB_Function) treeNode.getUserObject();
                if (func.getName().equals(name))
                    return treeNode;
            }
        }
        return null;
    }

    protected void showFuncNode(String name)
    {
        DefaultMutableTreeNode treeNode = findFuncNode(name);
        if (treeNode != null)
        {
            TreePath treePath = new TreePath(treeNode.getPath());
            setSelectionPath(treePath);
            expandPath(treePath);
            int row = getSelectionRows()[0];
            scrollRowToVisible(row + treeNode.getChildCount());
            scrollRowToVisible(row);
        }
    }

    protected SB_Action findAction(String name)
    {
        SB_Action action;
        Enumeration e = _actions.preorderEnumeration();
        DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) e.nextElement();
        while (e.hasMoreElements())
        {
            treeNode = (DefaultMutableTreeNode) e.nextElement();
            if (treeNode.getUserObject() instanceof SB_Action)
            {
                action = (SB_Action) treeNode.getUserObject();
                if (action.getName().equals(name))
                    return action;
            }
        }
        return null;
    }

    protected SB_Predicate findPredicate(String name)
    {
        SB_Predicate predicate;
        Enumeration e = _predicates.preorderEnumeration();
        DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) e.nextElement();
        while (e.hasMoreElements())
        {
            treeNode = (DefaultMutableTreeNode) e.nextElement();
            if (treeNode.getUserObject() instanceof SB_Predicate)
            {
                predicate = (SB_Predicate) treeNode.getUserObject();
                if (predicate.getName().equals(name))
                    return predicate;
            }
        }
        return null;
    }

    protected SB_Behavior findBehavior(String name)
    {
        SB_Behavior behavior;
        Enumeration e = _behaviors.preorderEnumeration();
        DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) e.nextElement();
        while (e.hasMoreElements())
        {
            treeNode = (DefaultMutableTreeNode) e.nextElement();
            if (treeNode.getUserObject() instanceof SB_Behavior)
            {
                behavior = (SB_Behavior) treeNode.getUserObject();
                if (behavior.getName().equals(name))
                    return behavior;
            }
        }
        return null;
    }

    protected void clearRunningState()
    {
        SB_Behavior behavior;
        Enumeration e = _behaviors.preorderEnumeration();
        DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) e.nextElement();
        while (e.hasMoreElements())
        {
            treeNode = (DefaultMutableTreeNode) e.nextElement();
            if (treeNode.getUserObject() instanceof SB_Behavior)
            {
                behavior = (SB_Behavior) treeNode.getUserObject();
                int count = behavior.getPolyCount();
                for (int i = 0; i < count; ++i)
                {
                    behavior.getPoly(i).getElements().clearRunningState();
                }
            }
        }
    }

    static String extractFuncName(String expr)
    {
        int index = expr.indexOf('(');
        if (index == -1)
            index = expr.length();
        return expr.substring(0, index);
    }

    public SB_Variable findVariable(SB_Polymorphism poly, String name)
    {
        // local
        SB_Variable var;
        Enumeration e = poly._locals.preorderEnumeration();
        DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) e.nextElement();
        while (e.hasMoreElements())
        {
            treeNode = (DefaultMutableTreeNode) e.nextElement();
            if (treeNode.getUserObject() instanceof SB_Variable)
            {
                var = (SB_Variable) treeNode.getUserObject();
                if (var.getName().equals(name))
                    return var;
            }
        }

        // parameter
        DefaultMutableTreeNode behavNode = findNode(poly._parent, _root);
        // if (behavNode == null)
        // return null;
        e = behavNode.preorderEnumeration();
        treeNode = (DefaultMutableTreeNode) e.nextElement();
        while (e.hasMoreElements())
        {
            treeNode = (DefaultMutableTreeNode) e.nextElement();
            if (treeNode.getUserObject() instanceof SB_Variable)
            {
                var = (SB_Variable) treeNode.getUserObject();
                if (var.getName().equals(name))
                    return var;
            }
        }

        // global
        return findGlobal(name);
    }

    protected SB_Variable findGlobal(String name)
    {
        SB_Global global;
        Enumeration e = _globals.preorderEnumeration();
        DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) e.nextElement();
        while (e.hasMoreElements())
        {
            treeNode = (DefaultMutableTreeNode) e.nextElement();
            if (treeNode.getUserObject() instanceof SB_Global)
            {
                global = (SB_Global) treeNode.getUserObject();
                if (global.getName().equals(name))
                    return global;
            }
        }
        return null;
    }

    protected boolean selectVariable(SB_Variable var)
    {
        DefaultMutableTreeNode treeNode = findNode(var, _root);
        if (treeNode == null)
            return false;
        TreePath treePath = new TreePath(treeNode.getPath());
        setSelectionPath(treePath);
        scrollPathToVisible(treePath);
        return true;
    }

    protected int findOccurrences(Pattern pattern, String strReplace) throws SB_CancelException
    {
        int total = 0;
        SB_Behavior behavior;
        Enumeration e = _behaviors.preorderEnumeration();
        DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) e.nextElement();
        while (e.hasMoreElements())
        {
            treeNode = (DefaultMutableTreeNode) e.nextElement();
            if (treeNode.getUserObject() instanceof SB_Behavior)
            {
                behavior = (SB_Behavior) treeNode.getUserObject();
                total += behavior.findOccurrences(pattern, strReplace);
            }
        }
        return total;
    }

    protected void updateProjectTitle()
    {
        String name = ComponentRegistry.getProjectBar()._fileSBP.getName();
        name = name.substring(0, name.length() - 4);
        _root.setUserObject("Project '" + name + "' Catalog");
        DefaultTreeModel treeModel = (DefaultTreeModel) this.treeModel;
        treeModel.nodeChanged(_root);
    }

    protected void updatePolyIndices(int type, int index, String name1, String name2)
    {
        DefaultTreeModel treeModel = (DefaultTreeModel) this.treeModel;
        DefaultMutableTreeNode globalNode = null;
        if (type != kPolyIndexInsert)
            globalNode = (DefaultMutableTreeNode) _globals.getChildAt(index);
        SB_Global global;
        switch (type)
        {
        case kPolyIndexInsert:
            global = new SB_Global("g" + name1);
            global.setType(SB_Variable.kString);
//            global.setTypeName(_typeManager.getTypeName(SB_VarType.kString));
            global.setPolymorphic(true);
            treeModel.insertNodeInto(new DefaultMutableTreeNode(global), _globals, index);
            break;
        case kPolyIndexDelete:
            treeModel.removeNodeFromParent(globalNode);
            break;
        case kPolyIndexRename:
            global = (SB_Global) globalNode.getUserObject();
            if (global.getName().equals("g" + name1))
            {
                global.setName("g" + name2);
                treeModel.nodeChanged(globalNode);
            }
            break;
        case kPolyIndexMoveUp:
            handleMenuItem(_moveUpItem, new TreePath(globalNode.getPath()));
            break;
        case kPolyIndexMoveDown:
            handleMenuItem(_moveDownItem, new TreePath(globalNode.getPath()));
            break;
        default:
            break;
        }

        SB_Behavior behavior;
        Enumeration e = _behaviors.preorderEnumeration();
        DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) e.nextElement();
        while (e.hasMoreElements())
        {
            treeNode = (DefaultMutableTreeNode) e.nextElement();
            if (treeNode.getUserObject() instanceof SB_Behavior)
            {
                behavior = (SB_Behavior) treeNode.getUserObject();
                behavior.updatePolyIndices(type, index, name1, name2);
            }
        }

        SB_TabbedCanvas tabbedCanvas = ComponentRegistry.getContent();
        SB_Canvas canvas;
        int size = tabbedCanvas.getTabCount();
        for (int i = 0; i < size; ++i)
        {
            canvas = (SB_Canvas) ((JScrollPane) tabbedCanvas.getComponentAt(i)).getViewport()
                    .getView();
            tabbedCanvas.setTitleAt(i, canvas._poly.getIndicesLabel());
        }

        setAPDModified(true);
    }

    protected void createBehaviorCombo()
    {
        _comboBehav = new JComboBox();
        _comboBehav.setEditable(true);
        _comboBehav.setMaximumSize(new Dimension(375, 25));
        _comboBehav.setPreferredSize(new Dimension(375, 25));
        _comboBehav.addItem("Main");
        _comboBehav.addActionListener(this);
    }
    
    public JComboBox getBehaviorCombo() {
        return _comboBehav;
    }

    public void updateComboBehav()
    {
        JComboBox comboBehav = _comboBehav;
        if (comboBehav != null)
        {
            comboBehav.removeAllItems();

            Vector behaviors = new Vector();
            SB_Behavior behavior;
            Enumeration e = _behaviors.preorderEnumeration();
            DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) e.nextElement();
            while (e.hasMoreElements())
            {
                treeNode = (DefaultMutableTreeNode) e.nextElement();
                if (treeNode.getUserObject() instanceof SB_Behavior)
                {
                    behavior = (SB_Behavior) treeNode.getUserObject();
                    behaviors.addElement(behavior);
                }
            }

            Collections.sort(behaviors);

            int size = behaviors.size();
            for (int i = 0; i < size; ++i)
            {
                behavior = (SB_Behavior) behaviors.get(i);
                comboBehav.addItem(behavior);
            }
        }
    }

    protected void insertToComboBehav(SB_Behavior behavior)
    {
        JComboBox comboBehav = _comboBehav;
        if (comboBehav != null)
        {
            boolean needToSelect = comboBehav.getSelectedItem() == behavior;

            comboBehav.removeItem(behavior);

            SB_Behavior next;
            int size = comboBehav.getItemCount();
            int i;
            for (i = 0; i < size; ++i)
            {
                next = (SB_Behavior) comboBehav.getItemAt(i);
                if (next.compareTo(behavior) > 0)
                    break;
            }
            comboBehav.insertItemAt(behavior, i);

            if (needToSelect)
                comboBehav.setSelectedItem(behavior);
        }
    }

    protected void showDescriptionDialog(SB_Function function)
    {
        if (_descriptionDialog == null)
        {
            _descriptionDialog = new JDialog(ComponentRegistry.getFrame(), true);

            JPanel descriptionPanel = new JPanel();
            descriptionPanel.setLayout(new BoxLayout(descriptionPanel, BoxLayout.Y_AXIS));
            descriptionPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
            JLabel label = new JLabel("Description:", JLabel.LEFT);
            label.setAlignmentX(Component.LEFT_ALIGNMENT);
            descriptionPanel.add(label);
            descriptionPanel.add(Box.createRigidArea(new Dimension(0, 10)));
            _descriptionTextArea = new JEditorPane();
            JScrollPane areaScrollPane = new JScrollPane(_descriptionTextArea);
            areaScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
            areaScrollPane.setAutoscrolls(true);
            areaScrollPane.setPreferredSize(new Dimension(271, 147));
            areaScrollPane.setAlignmentX(Component.LEFT_ALIGNMENT);
            descriptionPanel.add(areaScrollPane);

            JPanel buttonPanel = new JPanel();
            buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS));
            buttonPanel.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
            buttonPanel.add(Box.createHorizontalGlue());
            _descriptionOK = new JButton("OK");
            _descriptionOK.setFocusPainted(false);
            _descriptionOK.addActionListener(this);
            buttonPanel.add(_descriptionOK);
            buttonPanel.add(Box.createRigidArea(new Dimension(10, 0)));
            _descriptionCancel = new JButton("Cancel");
            _descriptionCancel.setFocusPainted(false);
            _descriptionCancel.addActionListener(this);
            buttonPanel.add(_descriptionCancel);

            _descriptionDialog.getContentPane().add(descriptionPanel, BorderLayout.CENTER);
            _descriptionDialog.getContentPane().add(buttonPanel, BorderLayout.SOUTH);
            _descriptionDialog.pack();

            Dimension dialogSize = _descriptionDialog.getSize();
            Rectangle frameBounds = ComponentRegistry.getFrame().getBounds();
            _descriptionDialog.setLocation(frameBounds.x + (frameBounds.width - dialogSize.width)
                    / 2, frameBounds.y + (frameBounds.height - dialogSize.height) / 2);
        }
        String functionName = function.toString();
        if (function.isCore())
            functionName += " (Reserved)";
        boolean editable = function.isCellEditable();
        _descriptionDialog.setTitle(functionName);
        _descriptionTextArea.setText(function.getDescription());
        _descriptionTextArea.setEnabled(editable);
        _descriptionTextArea.setCaretPosition(0);
        _descriptionTextArea.requestFocus();
        _descriptionOK.setEnabled(editable);
        _descriptionDialog.setVisible(true);
    }

    protected ClassSpecGeneratorDialog getClassSpecGeneratorDialog()
    {
        if (_classSpecGeneratorDialog == null)
        	_classSpecGeneratorDialog = ClassSpecGeneratorDialog.getInstance();
        return _classSpecGeneratorDialog;
    }

    protected ImportClassSpecDialog getImportClassSpecDialog()
    {
        if (_importClassSpecDialog == null)
        {
        	_importClassSpecDialog = ImportClassSpecDialog.getInstance();
        	_importClassSpecDialog.setClassSpeLoader(this);
        }
        return _importClassSpecDialog;
    }
    
    protected void showConstantValueDialog(final SB_Constant constant)
    {
        int type = constant.getType();
        
        // see if there is a custom editor for the constant type
        I_ExpressionEditor exprEditor = _editor.getEditorRegistry().getExpressionEditor(
        		EditorRegistry.EXPRESSION_TYPE_BINDING, 
        		_typeManager.getTypeName(SB_VarType.getTypeFromInt(type)), 
        		constant.getValue());
        if (exprEditor != null) {
        	exprEditor.editObject(constant.getValue(), new I_EditorListener() {
        		public void editingCanceled(I_ExpressionEditor source) {}
        		
        		public void editingCompleted(I_ExpressionEditor source, String result) {
        			constant.setValue(result);
        		}
        	});
        	return;
        }
    	
        if (_constantValueDialog == null)
        {
            _constantValueDialog = new JDialog(ComponentRegistry.getFrame(), true);

            JPanel constantValuePanel = new JPanel();
            constantValuePanel.setLayout(new BoxLayout(constantValuePanel, BoxLayout.Y_AXIS));
            constantValuePanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
            JLabel label = new JLabel("Constant Value:", JLabel.LEFT);
            label.setAlignmentX(Component.LEFT_ALIGNMENT);
            constantValuePanel.add(label);
            constantValuePanel.add(Box.createRigidArea(new Dimension(0, 10)));
            _constantValueTextField = new JTextField(30);
            _constantValueTextField.setPreferredSize(new Dimension(225, 23));
            _constantValueTextField.setAlignmentX(Component.LEFT_ALIGNMENT);
            _constantValueTextField.addActionListener(this);
            constantValuePanel.add(_constantValueTextField);

            JPanel buttonPanel = new JPanel();
            buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS));
            buttonPanel.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
            buttonPanel.add(Box.createHorizontalGlue());
            _constantValueOK = new JButton("OK");
            _constantValueOK.setFocusPainted(false);
            _constantValueOK.addActionListener(this);
            buttonPanel.add(_constantValueOK);
            buttonPanel.add(Box.createRigidArea(new Dimension(10, 0)));
            _constantValueCancel = new JButton("Cancel");
            _constantValueCancel.setFocusPainted(false);
            _constantValueCancel.addActionListener(this);
            buttonPanel.add(_constantValueCancel);

            _constantValueDialog.getContentPane().add(constantValuePanel, BorderLayout.NORTH);
            _constantValueDialog.getContentPane().add(buttonPanel, BorderLayout.SOUTH);
            _constantValueDialog.pack();

            Dimension dialogSize = _constantValueDialog.getSize();
            dialogSize.width = 225;
            _constantValueDialog.setSize(dialogSize);
            Rectangle frameBounds = ComponentRegistry.getFrame().getBounds();
            _constantValueDialog.setLocation(frameBounds.x + (frameBounds.width - dialogSize.width)
                    / 2, frameBounds.y + (frameBounds.height - dialogSize.height) / 2);
        }
        _constantValueDialog.setTitle(constant.toString());
        _constantValueTextField.setText(constant.getValue());
        _constantValueTextField.selectAll();
        _constantValueTextField.requestFocus();
        _constantValueDialog.setVisible(true);
    }

    protected void showInitialValueDialog(final SB_Global global)
    {
        if (global.isPolymorphic())
            return;
        if (global.getType() == SB_Variable.kEntity || global.getType() == SB_Variable.kData)
            return;

        // check if array or table
        int type = global.getType();
        
        // see if there is a custom editor for the variable
        I_ExpressionEditor exprEditor = _editor.getEditorRegistry().getExpressionEditor(
        		EditorRegistry.EXPRESSION_TYPE_BINDING, 
        		_typeManager.getTypeName(SB_VarType.getTypeFromInt(type)), 
        		global.getInitial());
        if (exprEditor != null) {
        	exprEditor.editObject(global.getInitial(), new I_EditorListener() {
        		public void editingCanceled(I_ExpressionEditor source) {}
        		
        		public void editingCompleted(I_ExpressionEditor source, String result) {
        			global.setInitial(result);
        		}
        	});
        	return;
        }
        
        if(type == SB_Variable.kArray){     	
       	   	if(_setInitialArrayValueDialog==null){
       	   		_setInitialArrayValueDialog = new SetInitialValueDialog(_editor, true);
           	}
       	   	SB_ToolBar toolBar = ComponentRegistry.getToolBar();
       	   	SB_Polymorphism poly = toolBar.getTabbedCanvas().getActiveCanvas()._poly;
       	   	_setInitialArrayValueDialog.initDialog(global, poly);
       	   	_setInitialArrayValueDialog.setVisible(true);
       	   	return;
        }
    
        if(type==SB_Variable.kTable){
       	   	if(_setInitialTableValueDialog==null){
       	   	_setInitialTableValueDialog = new SetInitialValueDialog(_editor, false);
           	}
       	   	SB_ToolBar toolBar = ComponentRegistry.getToolBar();
       	   	SB_Polymorphism poly = toolBar.getTabbedCanvas().getActiveCanvas()._poly;
       	   	_setInitialTableValueDialog.initDialog(global, poly);
       	   	_setInitialTableValueDialog.setVisible(true);
       	   	return;
        }
        
        if (_initialValueDialog == null)
        {
            _initialValueDialog = new JDialog(ComponentRegistry.getFrame(), true);

            JPanel initialValuePanel = new JPanel();
            initialValuePanel.setLayout(new BoxLayout(initialValuePanel, BoxLayout.Y_AXIS));
            initialValuePanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
            JLabel label = new JLabel("Initial Value:", JLabel.LEFT);
            label.setAlignmentX(Component.LEFT_ALIGNMENT);
            initialValuePanel.add(label);
            initialValuePanel.add(Box.createRigidArea(new Dimension(0, 10)));
            _initialValueTextField = new JTextField(30);
            _initialValueTextField.setPreferredSize(new Dimension(225, 23));
            _initialValueTextField.setAlignmentX(Component.LEFT_ALIGNMENT);
            _initialValueTextField.addActionListener(this);
            initialValuePanel.add(_initialValueTextField);

            JPanel buttonPanel = new JPanel();
            buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS));
            buttonPanel.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
            buttonPanel.add(Box.createHorizontalGlue());
            _initialValueOK = new JButton("OK");
            _initialValueOK.setFocusPainted(false);
            _initialValueOK.addActionListener(this);
            buttonPanel.add(_initialValueOK);
            buttonPanel.add(Box.createRigidArea(new Dimension(10, 0)));
            _initialValueCancel = new JButton("Cancel");
            _initialValueCancel.setFocusPainted(false);
            _initialValueCancel.addActionListener(this);
            buttonPanel.add(_initialValueCancel);

            _initialValueDialog.getContentPane().add(initialValuePanel, BorderLayout.NORTH);
            _initialValueDialog.getContentPane().add(buttonPanel, BorderLayout.SOUTH);
            _initialValueDialog.pack();

            Dimension dialogSize = _initialValueDialog.getSize();
            dialogSize.width = 225;
            _initialValueDialog.setSize(dialogSize);
            Rectangle frameBounds = ComponentRegistry.getFrame().getBounds();
            _initialValueDialog.setLocation(frameBounds.x + (frameBounds.width - dialogSize.width)
                    / 2, frameBounds.y + (frameBounds.height - dialogSize.height) / 2);
        }
        _initialValueDialog.setTitle(global.toString());
        _initialValueTextField.setText(global.getInitial());
        _initialValueTextField.selectAll();
        _initialValueTextField.requestFocus();
        _initialValueDialog.setVisible(true);
    }

    public boolean isBTNModified()
    {
        SB_Behavior behavior;
        Enumeration e = _behaviors.preorderEnumeration();
        DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) e.nextElement();
        while (e.hasMoreElements())
        {
            treeNode = (DefaultMutableTreeNode) e.nextElement();
            if (treeNode.getUserObject() instanceof SB_Behavior)
            {
                behavior = (SB_Behavior) treeNode.getUserObject();
                if (behavior.isBTNModified())
                    return true;
            }
        }
        return false;
    }

    protected void setSBPModified(boolean modified)
    {
        ComponentRegistry.getProjectBar().setSBPModified(modified);
    }

    protected void setAPDModified(boolean modified)
    {
        ComponentRegistry.getProjectBar().setAPDModified(modified);
    }

    public void setBTNModified(boolean modified)
    {
        SB_Behavior behavior;
        Enumeration e = _behaviors.preorderEnumeration();
        DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) e.nextElement();
        while (e.hasMoreElements())
        {
            treeNode = (DefaultMutableTreeNode) e.nextElement();
            if (treeNode.getUserObject() instanceof SB_Behavior)
            {
                behavior = (SB_Behavior) treeNode.getUserObject();
                behavior.setBTNModified(modified);
            }
        }
    }

    // actions, predicates, behaviors
    public void compileSIM(BufferedWriter out, DefaultMutableTreeNode parentNode,
            SB_ErrorInfo errorInfo, I_CompileValidator validator)
    {
        int count = 0;
        Enumeration e = parentNode.preorderEnumeration();
        DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) e.nextElement();
        while (e.hasMoreElements())
        {
            treeNode = (DefaultMutableTreeNode) e.nextElement();
            if (treeNode.getUserObject() instanceof SB_Function)
                ++count;
        }
        try
        {
            out.write(Integer.toString(count));
            out.newLine();
        } catch (IOException exception)
        {
            System.err.println("i/o exception");
        }

        SB_Function function;
        e = parentNode.preorderEnumeration();
        treeNode = (DefaultMutableTreeNode) e.nextElement();
        while (e.hasMoreElements())
        {
            treeNode = (DefaultMutableTreeNode) e.nextElement();
            if (treeNode.getUserObject() instanceof SB_Function)
            {
                function = (SB_Function) treeNode.getUserObject();
                function.compileSIM(out);
                compileParams(out, treeNode, validator);
                
                if (parentNode == _behaviors) {
                    ((SB_Behavior) function).compileSIM(out, errorInfo, _typeManager, validator);
                }
                else if (parentNode == _actions) {
                    validator.validateAction((SB_Action)function);
                }
                else if (parentNode == _predicates) {
                    validator.validatePredicate((SB_Predicate)function);
                }                
            }
        }
    }
    
    protected void compileClasses(BufferedWriter out, SB_ErrorInfo errorInfo, I_CompileValidator validator) throws IOException
    {
        out.write("Classes " + _rootPackage.getInUseClassCount());
    	out.newLine();
    	
    	_rootPackage.compileSIM(out, errorInfo, _typeManager);
    }

    protected void compileParams(BufferedWriter out, DefaultMutableTreeNode functionNode,I_CompileValidator validator)
    {
        try
        {
            // publish parameters
            int count = functionNode.getChildCount();
            out.write("\t\tParameters " + count);
            out.newLine();
            DefaultMutableTreeNode treeNode;
            SB_Parameter param;
            for (int i = 0; i < count; ++i)
            {
                // publish parameter name, type, direction
                treeNode = (DefaultMutableTreeNode) functionNode.getChildAt(i);
                param = (SB_Parameter) treeNode.getUserObject();
                out.write("\t\t\t");
                param.compileSIM(out, _typeManager);
                validator.validateParam(param,(SB_Function)functionNode.getUserObject());
                out.newLine();
            }
        } catch (IOException exception)
        {
            System.err.println("i/o exception");
        }
    }

    protected void compileGlobals(BufferedWriter out, SB_ErrorInfo errorInfo, I_CompileValidator validator)
    {
        int count = _globals.getChildCount();
        try
        {
            out.write("Globals " + count);
            out.newLine();
            DefaultMutableTreeNode treeNode;
            SB_Global global;
            for (int i = 0; i < count; ++i)
            {
                treeNode = (DefaultMutableTreeNode) _globals.getChildAt(i);
                global = (SB_Global) treeNode.getUserObject();
                if (global.isPolymorphic())
                    global.setInitial('"' + global.getName().substring(1) + '"');
                out.write("\t");
                global.compileSIM(getEditor(), out, errorInfo, _typeManager);
                out.newLine();
                if (global.isPolymorphic())
                    global.setInitial("");
            }
        } catch (IOException exception)
        {
            System.err.println("i/o exception");
        }
    }

    protected void validateConstants(SB_ErrorInfo errorInfo)
    {
        int count = _constants.getChildCount();
        DefaultMutableTreeNode treeNode;
        SB_Constant constant;
        for (int i = 0; i < count; ++i)
        {
            treeNode = (DefaultMutableTreeNode) _constants.getChildAt(i);
            constant = (SB_Constant) treeNode.getUserObject();
            constant.validate(getEditor(), errorInfo);
        }
    }

    public String constantReplace(String expr)
    {
        int count = _constants.getChildCount();
        DefaultMutableTreeNode treeNode;
        SB_Constant constant;
        for (int i = 0; i < count; ++i)
        {
            treeNode = (DefaultMutableTreeNode) _constants.getChildAt(i);
            constant = (SB_Constant) treeNode.getUserObject();
            expr = constant.replace(expr);
        }
        return expr;
    }

    protected int getDrawableCount(Class c)
    {
        int count = 0;
        SB_Behavior behavior;
        Enumeration e = _behaviors.preorderEnumeration();
        DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) e.nextElement();
        while (e.hasMoreElements())
        {
            treeNode = (DefaultMutableTreeNode) e.nextElement();
            if (treeNode.getUserObject() instanceof SB_Behavior)
            {
                behavior = (SB_Behavior) treeNode.getUserObject();
                count += behavior.getDrawableCount(c);
            }
        }
        return count;
    }

    protected void exportHeader(BufferedWriter out, String projectName) throws IOException
    {
        // ACTIONS
        out.write("// actions");
        out.newLine();
        // iterate through actions, writing reserved actions and then editable
        // actions
        // for (int p = 0; p <= 1; p++) // p = 0: reserved, p = 1: editable
        for (int p = 1; p <= 1; p++) // p = 0: reserved, p = 1: editable
        {
            SB_Action action;
            Enumeration e = _actions.preorderEnumeration();
            DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) e.nextElement();
            while (e.hasMoreElements())
            {
                treeNode = (DefaultMutableTreeNode) e.nextElement();
                if (treeNode.getUserObject() instanceof SB_Action)
                {
                    action = (SB_Action) treeNode.getUserObject();
                    int j = action.getId();
                    if ((p == 0 && j < START_USER_ID) || (p == 1 && j >= START_USER_ID))
                    {
                        // write line of the form "#define PROJECT_ACTION_name
                        // id"
                        out.write("#define " + projectName + "_ACTION_" + action.getName() + " "
                                + j);
                        out.newLine();
                    }
                }
            }
        }
        out.newLine();

        // PREDICATES
        out.write("// predicates");
        out.newLine();
        // iterate through actions, writing reserved predicates and then
        // editable predicates
        // for (int p = 0; p <= 1; p++) // p = 0: reserved, p = 1: editable
        for (int p = 1; p <= 1; p++) // p = 0: reserved, p = 1: editable
        {
            SB_Predicate predicate;
            Enumeration e = _predicates.preorderEnumeration();
            DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) e.nextElement();
            while (e.hasMoreElements())
            {
                treeNode = (DefaultMutableTreeNode) e.nextElement();
                if (treeNode.getUserObject() instanceof SB_Predicate)
                {
                    predicate = (SB_Predicate) treeNode.getUserObject();
                    int j = predicate.getId();
                    if ((p == 0 && j < START_USER_ID) || (p == 1 && j >= START_USER_ID))
                    {
                        // write line of the form "#define PROJECT_PRED_name id"
                        out.write("#define " + projectName + "_PRED_" + predicate.getName() + " "
                                + j);
                        out.newLine();
                    }
                }
            }
        }
    }

    public void exportClass(BufferedWriter out, String projectName) throws IOException
    {
        // ACTIONS
        out.write("// actions");
        out.newLine();
        // iterate through actions, writing reserved actions and then editable
        // actions
        // for (int p = 0; p <= 1; p++) // p = 0: reserved, p = 1: editable
        for (int p = 1; p <= 1; p++) // p = 0: reserved, p = 1: editable
        {
            SB_Action action;
            Enumeration e = _actions.preorderEnumeration();
            DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) e.nextElement();
            while (e.hasMoreElements())
            {
                treeNode = (DefaultMutableTreeNode) e.nextElement();
                if (treeNode.getUserObject() instanceof SB_Action)
                {
                    action = (SB_Action) treeNode.getUserObject();
                    int j = action.getId();
                    if ((p == 0 && j < START_USER_ID) || (p == 1 && j >= START_USER_ID))
                    {
                        out.write("\tpublic static final int ACTION_" + action.getName() + " = "
                                + j + ";");
                        out.newLine();
                    }
                }
            }
        }
        out.newLine();

        // PREDICATES
        out.write("// predicates");
        out.newLine();
        // iterate through actions, writing reserved predicates and then
        // editable predicates
        // for (int p = 0; p <= 1; p++) // p = 0: reserved, p = 1: editable
        for (int p = 1; p <= 1; p++) // p = 0: reserved, p = 1: editable
        {
            SB_Predicate predicate;
            Enumeration e = _predicates.preorderEnumeration();
            DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) e.nextElement();
            while (e.hasMoreElements())
            {
                treeNode = (DefaultMutableTreeNode) e.nextElement();
                if (treeNode.getUserObject() instanceof SB_Predicate)
                {
                    predicate = (SB_Predicate) treeNode.getUserObject();
                    int j = predicate.getId();
                    if ((p == 0 && j < START_USER_ID) || (p == 1 && j >= START_USER_ID))
                    {
                        out.write("\tpublic static final int PRED_" + predicate.getName() + " = "
                                + j + ";");
                        out.newLine();
                    }
                }
            }
        }
    }

    public void exportXML_APD(BufferedWriter out) throws IOException
    {
        out.write("\t\t<actions>");
        out.newLine();
        exportFunctions(out, _actions);
        out.write("\t\t</actions>");
        out.newLine();

        out.write("\t\t<predicates>");
        out.newLine();
        exportFunctions(out, _predicates);
        out.write("\t\t</predicates>");
        out.newLine();

        out.write("\t\t<constants>");
        out.newLine();
        DefaultMutableTreeNode childNode;
        SB_Constant constant;
        int count = _constants.getChildCount();
        for (int i = 0; i < count; ++i)
        {
            childNode = (DefaultMutableTreeNode) _constants.getChildAt(i);
            constant = (SB_Constant) childNode.getUserObject();
            out.write("\t\t\t\t<constant>");
            out.newLine();
            out.write("\t\t\t\t\t<name>" + constant.getName() + "</name>");
            out.newLine();
            out.write("\t\t\t\t\t<type>" + constant.getType() + "</type>");
            out.newLine();
            String value = constant.getValue();

            value = value.replaceAll("&", "&amp;");
            value = value.replaceAll("<", "&lt;");
            out.write("\t\t\t\t\t<value>" + value + "</value>");
            out.newLine();
            out.write("\t\t\t\t</constant>");
            out.newLine();
        }
        out.write("\t\t</constants>");
        out.newLine();
    }

    public void exportXML_SBP(BufferedWriter out) throws IOException
    {
        out.write("\t<sbp>");
        out.newLine();

        out.write("\t\t<main>" + _main.getName() + "</main>");
        out.newLine();

        out.write("\t\t<behaviors>");
        out.newLine();
        exportFunctions(out, _behaviors);
        out.write("\t\t</behaviors>");
        out.newLine();

        out.write("\t\t<globals>");
        out.newLine();
        DefaultMutableTreeNode childNode;
        SB_Global global;
        int count = _globals.getChildCount();
        for (int i = 0; i < count; ++i)
        {
            childNode = (DefaultMutableTreeNode) _globals.getChildAt(i);
            global = (SB_Global) childNode.getUserObject();
            if (global.isPolymorphic())
                continue;
            out.write("\t\t\t\t<global>");
            out.newLine();
            out.write("\t\t\t\t\t<name>" + global.getName() + "</name>");
            out.newLine();
            out.write("\t\t\t\t\t<type>" + global.getType() + "</type>");
            out.newLine();
            String initial = global.getInitialValue();

            initial = initial.replaceAll("&", "&amp;");
            initial = initial.replaceAll("<", "&lt;");
            out.write("\t\t\t\t\t<initial>" + initial + "</initial>");
            out.newLine();
            out.write("\t\t\t\t</global>");
            out.newLine();
        }
        out.write("\t\t</globals>");
        out.newLine();

        File fileSIM = ComponentRegistry.getProjectBar()._fileSIM;
        if (fileSIM != null)
        {
            out.write("\t\t<sim>" + fileSIM.getAbsoluteFile() + "</sim>");
            out.newLine();
        }

        out.write("\t</sbp>");
        out.newLine();
    }

    public void exportXML_BTNs(BufferedWriter out) throws IOException
    {
        Object userObject;
        Enumeration e = _behaviors.preorderEnumeration();
        DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) e.nextElement();
        while (e.hasMoreElements())
        {
            treeNode = (DefaultMutableTreeNode) e.nextElement();
            userObject = treeNode.getUserObject();
            if (userObject instanceof SB_Behavior)
            {
                out.write("\t<btn>");
                out.newLine();
                out.write("\t\t<behavior>");
                out.newLine();
                ((SB_Behavior) userObject).exportXML_BTN(out);
                exportParams(out, treeNode, 3);
                ((SB_Behavior) userObject).exportPolys(out);
                out.write("\t\t</behavior>");
                out.newLine();
                out.write("\t</btn>");
                out.newLine();
            }
        }
    }

    protected void exportFunctions(BufferedWriter out, DefaultMutableTreeNode treeNode)
            throws IOException
    {
        int count = treeNode.getChildCount();
        for (int i = 0; i < count; ++i)
            exportChild(out, (DefaultMutableTreeNode) treeNode.getChildAt(i));
    }

    protected void exportChild(BufferedWriter out, DefaultMutableTreeNode childNode)
            throws IOException
    {
        int level = childNode.getLevel();
        Object userObject = childNode.getUserObject();
        if (userObject instanceof SB_Folder)
        {
            SB_ProjectBar.writeTabs(out, level + 1);
            out.write("<folder>");
            out.newLine();
            SB_ProjectBar.writeTabs(out, level + 2);
            String name = userObject.toString();

            name = name.replaceAll("&", "&amp;");
            name = name.replaceAll("<", "&lt;");
            out.write("<name>" + name + "</name>");
            out.newLine();
            int count = childNode.getChildCount();
            for (int i = 0; i < count; ++i)
                exportChild(out, (DefaultMutableTreeNode) childNode.getChildAt(i));
            SB_ProjectBar.writeTabs(out, level + 1);
            out.write("</folder>");
            out.newLine();
        } else if (userObject instanceof SB_Function)
        {
            String tag = ((SB_Function) userObject).getTag();
            SB_ProjectBar.writeTabs(out, level + 1);
            out.write("<" + tag + ">");
            out.newLine();
            ((SB_Function) userObject).exportXML(out, level + 2);
            if (!(userObject instanceof SB_Behavior))
                exportParams(out, childNode, level + 2);
            SB_ProjectBar.writeTabs(out, level + 1);
            out.write("</" + tag + ">");
            out.newLine();
        }
    }

    protected void exportParams(BufferedWriter out, DefaultMutableTreeNode treeNode, int n)
            throws IOException
    {
        DefaultMutableTreeNode childNode;
        SB_Parameter param;
        int count = treeNode.getChildCount();
        for (int i = 0; i < count; ++i)
        {
            childNode = (DefaultMutableTreeNode) treeNode.getChildAt(i);
            param = (SB_Parameter) childNode.getUserObject();
            SB_ProjectBar.writeTabs(out, n);
            out.write("<param>");
            out.newLine();
            SB_ProjectBar.writeTabs(out, n + 1);
            out.write("<name>" + param.getName() + "</name>");
            out.newLine();
            SB_ProjectBar.writeTabs(out, n + 1);
            out.write("<type>" + param.getType() + "</type>");
            out.newLine();
            SB_ProjectBar.writeTabs(out, n + 1);
            out.write("<dir>" + param.getDirection() + "</dir>");
            out.newLine();
            SB_ProjectBar.writeTabs(out, n);
            out.write("</param>");
            out.newLine();
        }
    }

    public void autoscroll(Point cursorLocn)
    {
        Rectangle rect = getVisibleRect();
        if (cursorLocn.y > rect.y + rect.height / 2)
        {
            Dimension dim = getSize();
            rect.y = Math.min(rect.y + 10, dim.height - rect.height);
        } else
            rect.y = Math.max(rect.y - 10, 0);
        scrollRectToVisible(rect);
    }

    public Insets getAutoscrollInsets()
    {
        Rectangle rect = getVisibleRect();
        Dimension dim = getSize();
        Insets inset = new Insets(rect.y + 10, 0, dim.height - (rect.y + rect.height) + 10, 0);
        return inset;
    }

    public void dragEnter(DragSourceDragEvent event)
    {
    }

    public void dragOver(DragSourceDragEvent event)
    {
        Point location = event.getLocation();
        Point screenLocation = getLocationOnScreen();
        TreePath treePath = getPathForLocation(location.x - screenLocation.x, location.y
                - screenLocation.y);
        if (treePath != null
                && ((_dragNode.getUserObject() instanceof SB_Function) || (_dragNode
                        .getUserObject() instanceof SB_Folder)))
        {
            DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) treePath
                    .getLastPathComponent();
            if (treePath.getPathCount() == 2 || (treeNode.getUserObject() instanceof SB_Folder))
            {
                setSelectionPath(treePath);
                if (treeNode.getSharedAncestor(_dragNode) != _root)
                    event.getDragSourceContext().setCursor(DragSource.DefaultCopyDrop);
                else
                    event.getDragSourceContext().setCursor(DragSource.DefaultCopyNoDrop);
            } else
            {
                setSelectionInterval(_dragRow, _dragRow);
                event.getDragSourceContext().setCursor(DragSource.DefaultCopyDrop);
            }
            return;
        }

        setSelectionInterval(_dragRow, _dragRow);

        if (ComponentRegistry.getContent().getActiveCanvas()._allowDrop)
            event.getDragSourceContext().setCursor(DragSource.DefaultCopyDrop);
        else
            event.getDragSourceContext().setCursor(DragSource.DefaultCopyNoDrop);
    }

    public void dragExit(DragSourceEvent event)
    {

    }

    public void dragDropEnd(DragSourceDropEvent event)
    {
        if (event.getDropSuccess())
        {
        }

        _dragNode = null;
        _dragRow = -1;

        SB_Canvas canvas = ComponentRegistry.getContent().getActiveCanvas();
        canvas._allowDrop = true;
        canvas._dropType = SB_Canvas.kDropUnknown;
    }

    public void dropActionChanged(DragSourceDragEvent event)
    {
    }

    public void dragGestureRecognized(DragGestureEvent event)
    {
        if (_holdDrag)
        {
            _holdDrag = false;
            return;
        }

        Point point = event.getDragOrigin();
        TreePath treePath = getPathForLocation(point.x, point.y);
        if (treePath == null)
            return;
        DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) treePath.getLastPathComponent();

        Object userObject = treeNode.getUserObject();
        int n = treeNode.getChildCount();
        SB_Element element = null;
        String expr = null;
        StringSelection text = null;
        boolean checkSupported = true;
        SB_Canvas canvas = ComponentRegistry.getContent().getActiveCanvas();
        canvas._allowDrop = true;
        if (userObject instanceof SB_Action)
        {
            element = new SB_Rectangle();
            expr = ((SB_Action) userObject).getWildName(n);
            text = new StringSelection("Action:" + expr);
            canvas._dropType = SB_Canvas.kDropAction;
        } else if (userObject instanceof SB_Predicate)
        {
            element = new SB_Condition();
            expr = ((SB_Predicate) userObject).getWildName(n);
            text = new StringSelection("Predicate:" + expr);
            canvas._dropType = SB_Canvas.kDropPredicate;
        } else if (userObject instanceof SB_Behavior)
        {
            element = new SB_Rectangle();
            ((SB_Rectangle) element)._complex = true;
            expr = ((SB_Behavior) userObject).getWildName(n);
            text = new StringSelection("Behavior:" + expr);
            canvas._dropType = SB_Canvas.kDropBehavior;
        } else if (userObject instanceof SB_Parameter)
        {
            SB_Parameter parameter = (SB_Parameter) userObject;
            DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) treeNode.getParent();
            SB_Function function = (SB_Function) parentNode.getUserObject();
            if (function == canvas._poly._parent)
            {
                text = new StringSelection("Parameter:" + parameter.getName());
                canvas._dropType = SB_Canvas.kDropParameter;
            } else
            {
                text = new StringSelection("ParameterNA:" + parameter.getName());
                canvas._dropType = SB_Canvas.kDropParameterNA;
            }
            checkSupported = false;
        } else if (userObject instanceof SB_Constant)
        {
            text = new StringSelection("Constant:" + ((UserObject) userObject).getName());
            checkSupported = false;
            canvas._dropType = SB_Canvas.kDropConstant;
        } else if (userObject instanceof SB_Global)
        {
            text = new StringSelection("Global:" + ((UserObject) userObject).getName());
            checkSupported = false;
            canvas._dropType = SB_Canvas.kDropGlobal;
        } else if (userObject instanceof SB_Folder)
        {
            text = new StringSelection("Folder:" + ((UserObject) userObject).getName());
            checkSupported = false;
            canvas._dropType = SB_Canvas.kDropFolder;
        }

        if (text != null)
        {
            if (checkSupported && DragSource.isDragImageSupported())
            {
                element.setExpr(getEditor(), expr);
                Graphics2D g2 = (Graphics2D) _image.getGraphics();
                g2.setPaint(Color.white);
                g2.fill(new Rectangle(100, 75));
                element.draw(g2);
                Rectangle rect = element.getRect();
                BufferedImage subimage = _image.getSubimage(rect.x - 1, rect.y - 1, rect.width + 2,
                    rect.height + 2);
                Point center = new Point(-rect.width / 2 - 1, -rect.height / 2 - 1);
                _dragSource.startDrag(event, DragSource.DefaultCopyDrop, subimage, center, text,
                    this);
            } else
                _dragSource.startDrag(event, DragSource.DefaultCopyDrop, text, this);
            _dragNode = treeNode;
            _dragRow = getRowForPath(treePath);
            setEditable(false);
        }
    }

    public void dragEnter(DropTargetDragEvent event)
    {
        event.acceptDrag(DnDConstants.ACTION_MOVE);
    }

    public void dragExit(DropTargetEvent event)
    {
    }

    public void dragOver(DropTargetDragEvent event)
    {
    }

    public void drop(DropTargetDropEvent event)
    {
        if ((_dragNode.getUserObject() instanceof SB_Function)
                || (_dragNode.getUserObject() instanceof SB_Folder))
        {
            TreePath treePath = getSelectionPath();
            if (treePath == null)
                return;
            DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) treePath
                    .getLastPathComponent();
            if (treeNode != _dragNode && treeNode != _dragNode.getParent()
                    && treeNode.getSharedAncestor(_dragNode) != _root)
            {
                if (_dragNode.isNodeDescendant(treeNode))
                {
                    JOptionPane.showMessageDialog(ComponentRegistry.getFrame(),
                        "Folder cannot be moved to one of its subfolders.   ", null,
                        JOptionPane.WARNING_MESSAGE);
                } else
                {
                    boolean expanded = isExpanded(_dragRow);
                    DefaultTreeModel treeModel = (DefaultTreeModel) this.treeModel;
                    treeModel.removeNodeFromParent(_dragNode);
                    ((UserObject) _dragNode.getUserObject()).setNameToNextAvailable(treeNode);
                    insertNodeInto(_dragNode, treeNode);
                    if (expanded)
                        expandPath(getSelectionPath());
                    setFolderModified(_dragNode);
                }
            }
        }
    }

    public void dropActionChanged(DropTargetDragEvent event)
    {
    }
    
    //
    // class related methods
    //
    
    /**
     * Shows a file chooser to the user so that user can choose the class
     * specification(s) to import. Once the user commits the selection,
     * the class specification(s) will be imported and the "Types" subtree
     * will be updated.
     */
    protected void importClassSpec()
    {
		JFileChooser fc = new JFileChooser(System.getProperty("user.dir"));
		fc.setMultiSelectionEnabled(true);
		
		if (fc.showOpenDialog(this) == JFileChooser.APPROVE_OPTION)
		{
			File[] classSpecFiles = fc.getSelectedFiles();
			loadClassSpec(classSpecFiles);
		}
	}
    
    public void loadClassSpec(File[] classSpecFiles)
    {			
		try{
		    SB_ClassMap classMap = new SB_ClassMap();
		    SB_Logger logger = new SB_Logger();
		    logger.register(System.out, SB_Logger.ERROR|SB_Logger.WARNING);
		    SB_ClassSpecificationFileImport importer = new SB_ClassSpecificationFileImport(classMap, logger);
		    
		    for (int i = 0; i < classSpecFiles.length; i ++)
		    	importer.ImportClassSpec(classSpecFiles[i].toURI().toURL());
		    
		    // updates root package
		    _rootPackage.addClasses(classMap, importer.getCommentMap(), this);

		    // register package and classes
		    registerPackage(_rootPackage);
		    	
		    // updates class map
	        updateClassMap();
		    	
		    // updates tree nodes
		    updateTypes();

		    // set the dirty flag
		    setSBPModified(true);
		}
		catch(Exception ex){
		   	ex.printStackTrace();
		   	JOptionPane.showMessageDialog(this, ex + "\n" + ex.getMessage(), "Import Error", JOptionPane.ERROR_MESSAGE);
		}
	}
	
    
    /**
     * Pops up a dialog enabling the user to edit the type ID for the specified
     * class.  
     * @param classToEdit
     */
    private void editClassId(SB_Class classToEdit)
    {
        int typeId = classToEdit.getTypeId().getState();
        
        _editClassIdDialog = new JDialog(ComponentRegistry.getFrame(), true);

        JPanel editPanel = new JPanel();
        editPanel.setLayout(new BoxLayout(editPanel, BoxLayout.Y_AXIS));
        editPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
        JLabel label = new JLabel("Class Type Id:", JLabel.LEFT);
        label.setAlignmentX(Component.LEFT_ALIGNMENT);
        editPanel.add(label);
        editPanel.add(Box.createRigidArea(new Dimension(0, 10)));
        _classIdField = new JTextField(6);
        _classIdField.setPreferredSize(new Dimension(225, 23));
        _classIdField.setAlignmentX(Component.LEFT_ALIGNMENT);
        _classIdField.addActionListener(this);
        editPanel.add(_classIdField);
        
        JPanel buttonPanel = new JPanel();
        buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS));
        buttonPanel.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
        buttonPanel.add(Box.createHorizontalGlue());
        _editClassIdOK = new JButton("OK");
        _editClassIdOK.setFocusPainted(false);
        _editClassIdOK.addActionListener(this);
        buttonPanel.add(_editClassIdOK);
        buttonPanel.add(Box.createRigidArea(new Dimension(10, 0)));
        _editClassIdCancel = new JButton("Cancel");
        _editClassIdCancel.setFocusPainted(false);
        _editClassIdCancel.addActionListener(this);
        buttonPanel.add(_editClassIdCancel);
        
        _editClassIdDialog.getContentPane().add(editPanel, BorderLayout.NORTH);
        _editClassIdDialog.getContentPane().add(buttonPanel, BorderLayout.SOUTH);
        _editClassIdDialog.pack();
        
        Dimension dialogSize = _editClassIdDialog.getSize();
        dialogSize.width = 225;
        _editClassIdDialog.setSize(dialogSize);
        Rectangle frameBounds = ComponentRegistry.getFrame().getBounds();
        _editClassIdDialog.setLocation(frameBounds.x + (frameBounds.width - dialogSize.width)
                / 2, frameBounds.y + (frameBounds.height - dialogSize.height) / 2);

        _editClassIdDialog.setTitle("Edit Type Id - " + classToEdit.getName());
        _classIdField.setText(Integer.toString(typeId));
        _classIdField.selectAll();
        _classIdField.requestFocus();
        _editClassIdDialog.setVisible(true);
    }
    
    /**
     * Changes the type ID for the given class to the specified value. Updates all 
     * elements referencing that type to use the new ID.
     * @param classToChange
     * @param newTypeId
     * @return true if the change is successful, false if the new ID is invalid
     */
    private boolean changeClassTypeId(SB_Class classToChange,int newTypeId)
    {
        int oldTypeId = classToChange.getTypeId().getState();
        
        boolean result = _typeManager.changeTypeId(classToChange,newTypeId);
        
        if (result)
        {
            classToChange.setTypeId(new SB_VarType(newTypeId));
            
            // update any globals that reference the changed type
            for (int i = 0; i < _globals.getChildCount(); ++i)
            {
                DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) _globals.getChildAt(i);
                SB_Global global = (SB_Global) treeNode.getUserObject();
                if (global.getType() == oldTypeId)
                    global.setType(newTypeId);
            }
            
            Enumeration behEnum = getBehaviors();
            while (behEnum.hasMoreElements())
            {
                DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) behEnum.nextElement();
                if (!(treeNode.getUserObject() instanceof SB_Behavior))
                    continue;
                
                SB_Behavior behavior = (SB_Behavior)treeNode.getUserObject();

                // update any behavior parameters that reference the changed type
                for (int paramIndex=0; paramIndex < treeNode.getChildCount(); ++paramIndex)
                {
                    DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) treeNode.getChildAt(paramIndex);
                    SB_Parameter param = (SB_Parameter)childNode.getUserObject();
                    if (param.getType() == oldTypeId)
                        param.setType(newTypeId);
                }
                
                // update any local variables that reference the changed type
                for (int polyIndex=0; polyIndex < behavior.getPolyCount(); polyIndex++)
                {
                    SB_Polymorphism poly = behavior.getPoly(polyIndex);                    
                    for (int localIndex=0; localIndex < poly.getLocals().getChildCount(); ++localIndex)
                    {
                        DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) poly.getLocals().getChildAt(localIndex);
                        SB_Variable local = (SB_Variable)childNode.getUserObject();
                        if (local.getType() == oldTypeId)
                            local.setType(newTypeId);                        
                    }
                }
            }          
            
            // update any action parameters that reference the changed type
            Enumeration actEnum = _actions.preorderEnumeration();
            while (actEnum.hasMoreElements())
            {
                DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) actEnum.nextElement();
                if (!(treeNode.getUserObject() instanceof SB_Action))
                    continue;

                for (int paramIndex=0; paramIndex < treeNode.getChildCount(); ++paramIndex)
                {
                    DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) treeNode.getChildAt(paramIndex);
                    SB_Parameter param = (SB_Parameter)childNode.getUserObject();
                    if (param.getType() == oldTypeId)
                        param.setType(newTypeId);
                }

            }
            
            // update any predicate parameters and return types that reference the changed type
            Enumeration predEnum = _predicates.preorderEnumeration();
            while (predEnum.hasMoreElements())
            {
                DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) predEnum.nextElement();
                if (!(treeNode.getUserObject() instanceof SB_Predicate))
                    continue;

                SB_Predicate pred = (SB_Predicate) treeNode.getUserObject();
                if (pred.getRetType() == oldTypeId)
                    pred.setRetType(newTypeId);

                for (int paramIndex=0; paramIndex < treeNode.getChildCount(); ++paramIndex)
                {
                    DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) treeNode.getChildAt(paramIndex);
                    SB_Parameter param = (SB_Parameter)childNode.getUserObject();
                    if (param.getType() == oldTypeId)
                        param.setType(newTypeId);
                }

            }
        }
        
        return result;
    }
    
    /*
     * Updates the nodes under the "Types" subtree.
     */
    private void updateTypes(){
    	if (_types.getChildCount() > 0)
    	{
        	_types.removeAllChildren();
        	((DefaultTreeModel)getModel()).nodeStructureChanged(_types);
    	}
    	Vector packageChildren = new Vector(_rootPackage.getChildren());
    	Collections.sort(packageChildren);
    	for (Iterator it = packageChildren.iterator(); it.hasNext();)
    		insertNodeInto(createTreeNode(it.next()), _types);
    }
    
    /*
     * Creates DefaultMutableTreeNode for the specified object and 
     * its children if applicable.
     */
    private DefaultMutableTreeNode createTreeNode(Object obj)
    {
    	DefaultMutableTreeNode node = new DefaultMutableTreeNode(obj);
    	
    	if (obj instanceof SB_Package){
        	Vector packageChildren = new Vector(((SB_Package)obj).getChildren());
        	Collections.sort(packageChildren);
    		for (Iterator it = packageChildren.iterator(); it.hasNext();)
    			insertNodeInto(createTreeNode(it.next()), node);
    	}
    	else if (obj instanceof SB_Class){
    		Vector members = new Vector(((SB_Class)obj).getMembers());
    		Collections.sort(members);
    		for (Iterator it = members.iterator(); it.hasNext();)
    			insertNodeInto(createTreeNode(it.next()), node);
    		
    		Vector methods = new Vector(((SB_Class)obj).getMethods());
    		Collections.sort(methods);
    		for (Iterator it = methods.iterator(); it.hasNext();)
    			insertNodeInto(createTreeNode(it.next()), node);
    	}
    	else if (obj instanceof SB_ClassMethod){
    		Vector params = new Vector(((SB_ClassMethod)obj).getParams());
    		Collections.sort(params);
    		for (Iterator it = params.iterator(); it.hasNext();)
    			insertNodeInto(createTreeNode(it.next()), node);
    	}
    	
    	return node;
    }
    
    /*
     * Removes the package or class in the given node from 
     * its parentNode and its parentPackage.
     */
    private void removePackageOrClass(DefaultMutableTreeNode node,
    		DefaultMutableTreeNode parentNode, SB_Package parentPackage)
    {
    	int[] indices = new int[]{parentNode.getIndex(node)};
    	if (indices[0] < 0)
    		return;
    	
    	try{
        	// unregister classes;
        	if (node.getUserObject() instanceof SB_Package)
        		unregisterPackage((SB_Package)node.getUserObject());
        	else if (node.getUserObject() instanceof SB_Class)
        		unregisterClass((SB_Class)node.getUserObject());
        	
        	// removes tree node;
        	parentNode.remove(indices[0]);
        	
        	// removes class/package from parent package;
        	parentPackage.getChildren().remove(node.getUserObject());
        	
        	// updates class map
        	updateClassMap();
        	
        	// notifies the change;
        	((DefaultTreeModel)getModel()).nodesWereRemoved(parentNode, 
        			indices, new Object[]{node});
        	
	    	// set the dirty flag
	    	setSBPModified(true);
    	}
	    catch(Exception ex){
	    	ex.printStackTrace();
	    	JOptionPane.showMessageDialog(this, ex + "\n" + ex.getMessage(), "Remove Package/Class Error", JOptionPane.ERROR_MESSAGE);
	    }
    }
    
    public void setTypeManager(SB_TypeManager typeManager){
    	_typeManager = typeManager;
    	_typeManager.addTypeChangeListener(this);
    }
    
    public void typeAdded(SB_TypeManager source, 
    		SB_TypeManager.TypeInfo typeInfo)
    {
    	updateRetTypeMenu();
    }
    
    public void typeRemoved(SB_TypeManager source, 
    		SB_TypeManager.TypeInfo typeInfo)
    {
    	updateRetTypeMenu();
    }

    public void typeChanged(SB_TypeManager source, 
    		SB_TypeManager.TypeInfo oldTypeInfo, 
    		SB_TypeManager.TypeInfo newTypeInfo)
    {
    	updateRetTypeMenu();
    }
    
    private void updateRetTypeMenu(){
    	clearRetTypeSubMenu();
    	populateRetTypeSubMenu();
    }

    /*
     * Registers classes in the give package to the type manager.
     * The classes that have been registered will not be registered again.
     */
    private void registerPackage(SB_Package pack){
    	if (!(pack == null || pack.getChildren() == null)){
        	for (int i = 0; i < pack.getChildren().size(); i ++){
        		Object obj = pack.getChildren().get(i);
        		if (obj instanceof SB_Package)
        			registerPackage((SB_Package)obj);
        		else if (obj instanceof SB_Class)
        			registerClass((SB_Class)obj);
        	}
    	}
    }

    /*
     * Registers the given class to the type manager.
     * The class that has been registered will not be registered again.
     */
    private void registerClass(SB_Class cls){
    	if (_typeManager.classExists(cls.getName()))
    	{
    		// import new class spec and overwrite the old class with the same package
    		if (cls.getTypeId() == null)
    			cls.setTypeId(_typeManager.getClassIdByPackage(cls.getName()));
    	}
    	else
    	{
			// this class spec is just imported or loaded from older version (SBP_VER<=5)
			if (cls.getTypeId() == null){
				SB_VarType typeId = _typeManager.addClass(cls.getAlias(), cls.getName(), false);
				cls.setTypeId(typeId);
			}
			// or this class spec is re-loaded from new version (SBP_VER>=6)
			else{
				_typeManager.addTypeWithId(cls.getAlias(), cls.getTypeId(),
						ETypeValid.kForVar.getState()|ETypeValid.kForParam.getState()|ETypeValid.kForRetVal.getState(),
						ETypeType.kClassType.getState(), false, cls.getName());
			}
    	}
    }
    
    /*
     * Unregisters classes in the given package from the type manager.
     */
    private void unregisterPackage(SB_Package pack){
    	if (!(pack == null || pack.getChildren() == null)){
        	for (int i = 0; i < pack.getChildren().size(); i ++){
        		Object obj = pack.getChildren().get(i);
        		if (obj instanceof SB_Package)
        			unregisterPackage((SB_Package)obj);
        		else if (obj instanceof SB_Class)
        			unregisterClass((SB_Class)obj);
        	}
    	}
    }
    
    /*
     * Unregisters the given class from the type manager.
     */
    private void unregisterClass(SB_Class cls){
    	SB_VarType id = _typeManager.getClassIdByPackage(cls.getName());
    	if (!SB_VarType.kNoSuchName.equals(id))
    		_typeManager.removeTypeById(id);
    }
    
    private void updateClassMap() throws ClassNotFoundException{
    	_classMap = new SB_ClassMap();
    	_rootPackage.toClassMap(_classMap, _typeManager);
    }
    
    public SB_ClassMap getClassMap(){
    	return _classMap;
    }
    
    /**
     * @param className	fully qualified class name, e.g., java.lang.String
     */
    public SB_Class lookupClass(String className){
    	return _rootPackage.lookupClass(className);
    }
    
    /**
     * @param classAlias	a short version of the class name, e.g., String
     */
    public SB_Class lookupClassByAlias(String classAlias){
    	return _rootPackage.lookupClassByAlias(classAlias);
    }

    public void nameChanged(UserObject userObject,String oldName)
    {
        if (userObject instanceof SB_Behavior)
            ComponentRegistry.getProjectBar().behaviorRenamed((SB_Behavior)userObject,oldName);
    }

    class SetInitialValueDialog extends SetValueDialog {
    	
    	protected SB_Global _global;
    	
    	/**
    	 * Constructor
    	 * @param editor SimBionicEditor needed to create SB_Autocomplete
    	 * @param isArray is this dialog for editing array?
    	 */
        public SetInitialValueDialog(SimBionicEditor editor, boolean isArray)
        {
        	super(editor, isArray);
        }
        
        /**
         * to be called to initialize the dialog. the table value will be reset
         * @param global SB_Global value to be set.
         */
        public void initDialog(SB_Global global, SB_Polymorphism poly){
        	_global = global;
            _tableModel.setValue(global.getInitialValue());
        	// needed to parse variable names
            _poly = poly;
        	SB_ParserEditorInterface.getParser().setPoly(poly);
        }
        
        @Override
        public void onOk(){
        	super.onOk();
        	String newValue = _tableModel.getValue();
            if (!_global.getInitial().equals(newValue))
            {
            	_global.setInitial(newValue);
                setSBPModified(true);
            }
        }
        
        /**
         * returns true if the given string is a variable name
         * see SetValueDialog&SetValueTableModel.SetValueAt for details
         * @param varName given string to check if it is a variable
         * @return true if the given string is a variable name
         */
        protected boolean isVariable(String varName){
        	return isGlobal(varName);
        }
    }
}
